321 lines
11 KiB
Java
321 lines
11 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|