Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1 | // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 | // Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions |
| 6 | // are met: |
| 7 | // 1. Redistributions of source code must retain the above copyright |
| 8 | // notice, this list of conditions and the following disclaimer. |
| 9 | // 2. Redistributions in binary form must reproduce the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer in the |
| 11 | // documentation and/or other materials provided with the distribution. |
| 12 | // |
| 13 | // THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| 14 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 16 | // DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 23 | |
| 24 | description( |
| 25 | "This test checks that parentheses are preserved when significant, and not added where inappropriate. " + |
| 26 | "We need this test because the JavaScriptCore parser removes all parentheses and the serializer then adds them back." |
| 27 | ); |
| 28 | |
| 29 | function compileAndSerialize(expression) |
| 30 | { |
| 31 | var f = eval("(function () { return " + expression + "; })"); |
| 32 | var serializedString = f.toString(); |
| 33 | serializedString = serializedString.replace(/[ \t\r\n]+/g, " "); |
| 34 | serializedString = serializedString.replace("function () { return ", ""); |
| 35 | serializedString = serializedString.replace("; }", ""); |
| 36 | return serializedString; |
| 37 | } |
| 38 | |
| 39 | function compileAndSerializeLeftmostTest(expression) |
| 40 | { |
| 41 | var f = eval("(function () { " + expression + "; })"); |
| 42 | var serializedString = f.toString(); |
| 43 | serializedString = serializedString.replace(/[ \t\r\n]+/g, " "); |
| 44 | serializedString = serializedString.replace("function () { ", ""); |
| 45 | serializedString = serializedString.replace("; }", ""); |
| 46 | return serializedString; |
| 47 | } |
| 48 | |
| 49 | var removesExtraParentheses = compileAndSerialize("(a + b) + c") == "a + b + c"; |
| 50 | |
| 51 | function testKeepParentheses(expression) |
| 52 | { |
| 53 | shouldBe("compileAndSerialize('" + expression + "')", |
| 54 | "'" + expression + "'"); |
| 55 | } |
| 56 | |
| 57 | function testOptionalParentheses(expression) |
| 58 | { |
| 59 | stripped_expression = removesExtraParentheses |
| 60 | ? expression.replace(/\(/g, '').replace(/\)/g, '') |
| 61 | : expression; |
| 62 | shouldBe("compileAndSerialize('" + expression + "')", |
| 63 | "'" + stripped_expression + "'"); |
| 64 | } |
| 65 | |
| 66 | function testLeftAssociativeSame(opA, opB) |
| 67 | { |
| 68 | testKeepParentheses("a " + opA + " b " + opB + " c"); |
| 69 | testOptionalParentheses("(a " + opA + " b) " + opB + " c"); |
| 70 | testKeepParentheses("a " + opA + " (b " + opB + " c)"); |
| 71 | } |
| 72 | |
| 73 | function testRightAssociativeSame(opA, opB) |
| 74 | { |
| 75 | testKeepParentheses("a " + opA + " b " + opB + " c"); |
| 76 | testKeepParentheses("(a " + opA + " b) " + opB + " c"); |
| 77 | testOptionalParentheses("a " + opA + " (b " + opB + " c)"); |
| 78 | } |
| 79 | |
| 80 | function testHigherFirst(opHigher, opLower) |
| 81 | { |
| 82 | testKeepParentheses("a " + opHigher + " b " + opLower + " c"); |
| 83 | testOptionalParentheses("(a " + opHigher + " b) " + opLower + " c"); |
| 84 | testKeepParentheses("a " + opHigher + " (b " + opLower + " c)"); |
| 85 | } |
| 86 | |
| 87 | function testLowerFirst(opLower, opHigher) |
| 88 | { |
| 89 | testKeepParentheses("a " + opLower + " b " + opHigher + " c"); |
| 90 | testKeepParentheses("(a " + opLower + " b) " + opHigher + " c"); |
| 91 | testOptionalParentheses("a " + opLower + " (b " + opHigher + " c)"); |
| 92 | } |
| 93 | |
| 94 | var binaryOperators = [ |
| 95 | [ "*", "/", "%" ], [ "+", "-" ], |
| 96 | [ "<<", ">>", ">>>" ], |
| 97 | [ "<", ">", "<=", ">=", "instanceof", "in" ], |
| 98 | [ "==", "!=", "===", "!==" ], |
| 99 | [ "&" ], [ "^" ], [ "|" ], |
| 100 | [ "&&" ], [ "||" ] |
| 101 | ]; |
| 102 | |
| 103 | for (i = 0; i < binaryOperators.length; ++i) { |
| 104 | var ops = binaryOperators[i]; |
| 105 | for (j = 0; j < ops.length; ++j) { |
| 106 | var op = ops[j]; |
| 107 | testLeftAssociativeSame(op, op); |
| 108 | if (j != 0) |
| 109 | testLeftAssociativeSame(ops[0], op); |
| 110 | if (i < binaryOperators.length - 1) { |
| 111 | var nextOps = binaryOperators[i + 1]; |
| 112 | if (j == 0) |
| 113 | for (k = 0; k < nextOps.length; ++k) |
| 114 | testHigherFirst(op, nextOps[k]); |
| 115 | else |
| 116 | testHigherFirst(op, nextOps[0]); |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | var assignmentOperators = [ "=", "*=", "/=" , "%=", "+=", "-=", "<<=", ">>=", ">>>=", "&=", "^=", "|=" ]; |
| 122 | |
| 123 | for (i = 0; i < assignmentOperators.length; ++i) { |
| 124 | var op = assignmentOperators[i]; |
| 125 | testRightAssociativeSame(op, op); |
| 126 | if (i != 0) |
| 127 | testRightAssociativeSame("=", op); |
| 128 | testLowerFirst(op, "+"); |
| 129 | shouldThrow("compileAndSerialize('a + b " + op + " c')"); |
| 130 | testKeepParentheses("(a + b) " + op + " c"); |
| 131 | testKeepParentheses("a + (b " + op + " c)"); |
| 132 | } |
| 133 | |
| 134 | var prefixOperators = [ "delete", "void", "typeof", "++", "--", "+", "-", "~", "!" ]; |
| 135 | var prefixOperatorSpace = [ " ", " ", " ", "", "", " ", " ", "", "" ]; |
| 136 | |
| 137 | for (i = 0; i < prefixOperators.length; ++i) { |
| 138 | var op = prefixOperators[i] + prefixOperatorSpace[i]; |
| 139 | testKeepParentheses("" + op + "a + b"); |
| 140 | testOptionalParentheses("(" + op + "a) + b"); |
| 141 | testKeepParentheses("" + op + "(a + b)"); |
| 142 | testKeepParentheses("!" + op + "a"); |
| 143 | testOptionalParentheses("!(" + op + "a)"); |
| 144 | } |
| 145 | |
| 146 | |
| 147 | testKeepParentheses("!a++"); |
| 148 | testOptionalParentheses("!(a++)"); |
| 149 | testKeepParentheses("(!a)++"); |
| 150 | |
| 151 | testKeepParentheses("!a--"); |
| 152 | testOptionalParentheses("!(a--)"); |
| 153 | testKeepParentheses("(!a)--"); |
| 154 | |
| 155 | testKeepParentheses("(-1)[a]"); |
| 156 | testKeepParentheses("(-1)[a] = b"); |
| 157 | testKeepParentheses("(-1)[a] += b"); |
| 158 | testKeepParentheses("(-1)[a]++"); |
| 159 | testKeepParentheses("++(-1)[a]"); |
| 160 | testKeepParentheses("(-1)[a]()"); |
| 161 | |
| 162 | testKeepParentheses("new (-1)()"); |
| 163 | |
| 164 | testKeepParentheses("(-1).a"); |
| 165 | testKeepParentheses("(-1).a = b"); |
| 166 | testKeepParentheses("(-1).a += b"); |
| 167 | testKeepParentheses("(-1).a++"); |
| 168 | testKeepParentheses("++(-1).a"); |
| 169 | testKeepParentheses("(-1).a()"); |
| 170 | |
| 171 | testKeepParentheses("(- 0)[a]"); |
| 172 | testKeepParentheses("(- 0)[a] = b"); |
| 173 | testKeepParentheses("(- 0)[a] += b"); |
| 174 | testKeepParentheses("(- 0)[a]++"); |
| 175 | testKeepParentheses("++(- 0)[a]"); |
| 176 | testKeepParentheses("(- 0)[a]()"); |
| 177 | |
| 178 | testKeepParentheses("new (- 0)()"); |
| 179 | |
| 180 | testKeepParentheses("(- 0).a"); |
| 181 | testKeepParentheses("(- 0).a = b"); |
| 182 | testKeepParentheses("(- 0).a += b"); |
| 183 | testKeepParentheses("(- 0).a++"); |
| 184 | testKeepParentheses("++(- 0).a"); |
| 185 | testKeepParentheses("(- 0).a()"); |
| 186 | |
| 187 | testOptionalParentheses("(1)[a]"); |
| 188 | testOptionalParentheses("(1)[a] = b"); |
| 189 | testOptionalParentheses("(1)[a] += b"); |
| 190 | testOptionalParentheses("(1)[a]++"); |
| 191 | testOptionalParentheses("++(1)[a]"); |
| 192 | |
| 193 | shouldBe("compileAndSerialize('(1)[a]()')", |
| 194 | removesExtraParentheses ? "'1[a]()'" : "'(1)[a]()'"); |
| 195 | |
| 196 | shouldBe("compileAndSerialize('new (1)()')", |
| 197 | removesExtraParentheses ? "'new 1()'" : "'new (1)()'"); |
| 198 | |
| 199 | testKeepParentheses("(1).a"); |
| 200 | testKeepParentheses("(1).a = b"); |
| 201 | testKeepParentheses("(1).a += b"); |
| 202 | testKeepParentheses("(1).a++"); |
| 203 | testKeepParentheses("++(1).a"); |
| 204 | testKeepParentheses("(1).a()"); |
| 205 | |
| 206 | for (i = 0; i < assignmentOperators.length; ++i) { |
| 207 | var op = assignmentOperators[i]; |
| 208 | testKeepParentheses("(-1) " + op + " a"); |
| 209 | testKeepParentheses("(- 0) " + op + " a"); |
| 210 | testKeepParentheses("1 " + op + " a"); |
| 211 | } |
| 212 | |
| 213 | shouldBe("compileAndSerializeLeftmostTest('({ }).x')", "'({ }).x'"); |
| 214 | shouldBe("compileAndSerializeLeftmostTest('x = { }')", "'x = { }'"); |
| 215 | shouldBe("compileAndSerializeLeftmostTest('(function () { })()')", "'(function () { })()'"); |
| 216 | shouldBe("compileAndSerializeLeftmostTest('x = function () { }')", "'x = function () { }'"); |
| 217 | |
| 218 | shouldBe("compileAndSerializeLeftmostTest('var a')", "'var a'"); |
| 219 | shouldBe("compileAndSerializeLeftmostTest('var a = 1')", "'var a = 1'"); |
| 220 | shouldBe("compileAndSerializeLeftmostTest('var a, b')", "'var a, b'"); |
| 221 | shouldBe("compileAndSerializeLeftmostTest('var a = 1, b = 2')", "'var a = 1, b = 2'"); |
| 222 | shouldBe("compileAndSerializeLeftmostTest('var a, b, c')", "'var a, b, c'"); |
| 223 | shouldBe("compileAndSerializeLeftmostTest('var a = 1, b = 2, c = 3')", "'var a = 1, b = 2, c = 3'"); |
| 224 | |
| 225 | shouldBe("compileAndSerializeLeftmostTest('const a = 1')", "'const a = 1'"); |
| 226 | shouldBe("compileAndSerializeLeftmostTest('const a = (1, 2)')", "'const a = (1, 2)'"); |
| 227 | shouldBe("compileAndSerializeLeftmostTest('const a, b = 1')", "'const a, b = 1'"); |
| 228 | shouldBe("compileAndSerializeLeftmostTest('const a = 1, b')", "'const a = 1, b'"); |
| 229 | shouldBe("compileAndSerializeLeftmostTest('const a = 1, b = 1')", "'const a = 1, b = 1'"); |
| 230 | shouldBe("compileAndSerializeLeftmostTest('const a = (1, 2), b = 1')", "'const a = (1, 2), b = 1'"); |
| 231 | shouldBe("compileAndSerializeLeftmostTest('const a = 1, b = (1, 2)')", "'const a = 1, b = (1, 2)'"); |
| 232 | shouldBe("compileAndSerializeLeftmostTest('const a = (1, 2), b = (1, 2)')", "'const a = (1, 2), b = (1, 2)'"); |
| 233 | |
| 234 | shouldBe("compileAndSerialize('(function () { new (a.b()).c })')", "'(function () { new (a.b()).c })'"); |