File indexing completed on 2024-04-28 15:28:37

0001 /*
0002 Note: this test is from Apple
0003 
0004 Test exception handling with various arithmetic and logic operators, it checks the following things:<br/>
0005 <ul>
0006     <li>In assignment expressions the lefthand side is not modified if the right hand side throws</li>
0007     <li>If the left hand side of a binary operator throws then the right hand should not be executed</li>
0008     <li>If valueOf/toString throws in the left hand expression of a binop it does not prevent evaluation of the right hand expression, but does prevent evaluation of toString/valueOf on the rhs.</li>
0009 </ul>
0010 */
0011 
0012 function captureGlobal() {
0013     global = this;
0014 }
0015 
0016 captureGlobal();
0017 
0018 if (global["debug"])
0019     log = debug;
0020 else
0021     log = print;
0022     
0023 fail = testFailed;
0024 pass = function(msg) { ++passCount; testPassed(msg); }
0025 
0026 var unaryOps = ['~', '+', '-'];
0027 var binaryOps = ['-', '+', '*', '/', '%', '|', '&', '^', '<', '<=', '>=', '>', '==', '!=', '<<', '>>'];
0028 
0029 var valueOfThrower = { valueOf: function() { throw "throw from valueOf"; } }
0030 var toStringThrower = { toString: function() { throw "throw from toString"; } }
0031 var testCount = 0;
0032 var passCount = 0;
0033 function createTest(expr) {
0034     // This creates a test case that ensures that a binary operand will execute the left hand expression first,
0035     // and will not execute the right hand side if the left hand side throws.
0036     var functionPrefix = "(function(){ executedRHS = false; var result = 'PASS'; try { result = ";
0037     var functionPostfix = "; fail('Did not throw exception with \"' + expr + '\"') } catch (e) { " +
0038                           " if (result != 'PASS' && executedRHS) { fail('\"'+expr+'\" threw exception, but modified assignment target and executed RHS'); " +
0039                           " } else if (result != 'PASS') { fail('\"'+expr+'\" threw exception, but modified assignment target'); " +
0040                           " } else if (executedRHS) { fail('\"'+expr+'\" threw exception, but executed right hand half of expression')" +
0041                           " } else { pass('Handled \"'+ expr +'\" correctly.') } } })";
0042     testCount++;
0043     try {
0044         return eval(functionPrefix + expr + functionPostfix);
0045     } catch(e) {
0046         throw new String(expr);
0047     }
0048 }
0049 
0050 function createTestWithRHSExec(expr) {
0051     // This tests that we evaluate the right hand side of a binary expression before we
0052     // do any type conversion with toString and/or valueOf which may throw.
0053     var functionPrefix = "(function(){ executedRHS = false; var result = 'PASS'; try { result = ";
0054     var functionPostfix = "; fail('Did not throw exception with \"' + expr + '\"') } catch (e) { " +
0055                           " if (result != 'PASS') { fail('\"'+expr+'\" threw exception, but modified assignment target'); " +
0056                           " } else if (!executedRHS) { fail('\"'+expr+'\" threw exception, and failed to execute RHS when expected')" +
0057                           " } else { pass('Handled \"'+ expr +'\" correctly.') } } })";
0058     testCount++;
0059     try {
0060         return eval(functionPrefix + expr + functionPostfix);
0061     } catch(e) {
0062         throw new String(expr);
0063     }
0064 }
0065 
0066 __defineGetter__('throwingProperty', function(){ throw "throwing resolve"; });
0067 
0068 var throwingPropStr = 'throwingProperty';
0069 var valueOfThrowerStr = 'valueOfThrower';
0070 var toStringThrowerStr = 'toStringThrower';
0071 var throwingObjGetter = '({get throwingProperty(){ throw "throwing property" }}).throwingProperty';
0072 
0073 createTest(throwingPropStr)();
0074 createTest(throwingObjGetter)();
0075 createTest("!undefinedProperty")();
0076 
0077 for (var i = 0; i < unaryOps.length; i++) {
0078     createTest(unaryOps[i]+valueOfThrowerStr)();
0079     createTest(unaryOps[i]+toStringThrowerStr)();
0080     createTest(unaryOps[i]+throwingPropStr)();
0081     createTest(unaryOps[i]+throwingObjGetter)();
0082 }
0083 rhsNonZeroNum = { valueOf: function(){ executedRHS = true; return 1; } };
0084 rhsZeroNum = { valueOf: function(){ executedRHS = true; return 0; } };
0085 rhsToStringThrower = { toString: function(){ executedRHS = true; return 'string'; }};
0086 getterThrower = { get value() { throw "throwing in getter"; }};
0087 var getterThrowerStr = "getterThrower.value";
0088 rhsGetterTester = { get value() { executedRHS = true; return 'string'; }};
0089 var rhsGetterTesterStr = "rhsGetterTester.value";
0090 
0091 for (var i = 0; i < binaryOps.length; i++) {
0092     var numVal = binaryOps[i] == '||' ? '0' : '1';
0093     createTest(numVal + " " + binaryOps[i] + " " + valueOfThrowerStr )();
0094     createTest(numVal + " " + binaryOps[i] + " " + toStringThrowerStr)();
0095     createTest(numVal + " " + binaryOps[i] + " " + throwingPropStr   )();
0096     createTest(numVal + " " + binaryOps[i] + " " + throwingObjGetter )();
0097     createTest(numVal + " " + binaryOps[i] + " " + getterThrowerStr )();
0098 
0099     createTest("'string' " + binaryOps[i] + " " + valueOfThrowerStr )();
0100     createTest("'string' " + binaryOps[i] + " " + toStringThrowerStr)();
0101     createTest("'string' " + binaryOps[i] + " " + throwingPropStr   )();
0102     createTest("'string' " + binaryOps[i] + " " + throwingObjGetter )();
0103     createTest("'string' " + binaryOps[i] + " " + getterThrowerStr )();
0104     
0105     numVal = binaryOps[i] == '||' ? 'rhsZeroNum' : 'rhsNonZeroNum';
0106     createTest(valueOfThrowerStr  + " " + binaryOps[i] + " " + numVal)();
0107     createTest(toStringThrowerStr + " " + binaryOps[i] + " " + numVal)();
0108     createTest(throwingPropStr    + " " + binaryOps[i] + " " + numVal)();
0109     createTest(throwingObjGetter  + " " + binaryOps[i] + " " + numVal)();
0110     createTest(getterThrowerStr  + " " + binaryOps[i] + " " + numVal)();
0111     createTest(valueOfThrowerStr  + " " + binaryOps[i] + " rhsToStringThrower")();
0112     createTest(toStringThrowerStr + " " + binaryOps[i] + " rhsToStringThrower")();
0113     createTest(throwingPropStr    + " " + binaryOps[i] + " rhsToStringThrower")();
0114     createTest(throwingObjGetter  + " " + binaryOps[i] + " rhsToStringThrower")();
0115     createTest(getterThrowerStr  + " " + binaryOps[i] + " rhsToStringThrower")();
0116     createTestWithRHSExec(valueOfThrowerStr  + " " + binaryOps[i] + " " + rhsGetterTesterStr)();
0117     createTestWithRHSExec(toStringThrowerStr + " " + binaryOps[i] + " " + rhsGetterTesterStr)();
0118 
0119 }
0120 
0121 var executionOrder = "";
0122 __defineGetter__("nonThrowingIndexBase", function(){ 
0123     executionOrder += "nonThrowingIndexBase, "; 
0124     return {
0125         get nonThrowingTestIndex(){ executionOrder += "get nonThrowingTestIndex, "; return undefined; },
0126         get throwingTestIndex(){ executionOrder += "get nonThrowingTestIndex, "; throw {}; return undefined; },
0127         set nonThrowingTestIndex(){ executionOrder += "set nonThrowingTestIndex, "; return undefined; },
0128         set throwingTestIndex(){ executionOrder += "set nonThrowingTestIndex, "; throw {}; return undefined; }
0129     }
0130   });
0131 __defineGetter__("throwingIndexBase", function(){ 
0132     executionOrder += "throwingIndexBase, "; 
0133     throw {};
0134   });
0135 
0136 __defineGetter__("nonThrowingIndexNoThrowProperty", function(){
0137     return {
0138         toString: function() {executionOrder += "nonThrowingIndexNoThrowProperty, "; return "nonThrowingTestIndex"; }
0139     }
0140 });
0141 
0142 __defineGetter__("nonThrowingIndexThrowProperty", function(){ 
0143     return {
0144         toString: function() {executionOrder += "nonThrowingIndexThrowProperty, "; return "throwingTestIndex"; }
0145     }
0146 });
0147 
0148 __defineGetter__("throwingIndex", function(){
0149     return {
0150         toString: function() {executionOrder += "throwingIndex, "; throw {};}
0151     }
0152 });
0153 
0154 __defineGetter__("valueForAssignment", function(){ executionOrder += "valueForAssignment, "; return 1; })
0155 
0156 function createTestWithExecOrderTest(expr, expected) {
0157     // This tests that we evaluate the right hand side of a binary expression before we
0158     // do any type conversion with toString and/or valueOf which may throw.
0159     var functionPrefix = "(function(){ executionOrder = ''; var result = 'PASS'; try { result = ";
0160     var functionPostfix = "; } catch (e) { executionOrder +=  '***throw***'; } if (executionOrder == expected) pass(expr); else " +
0161                           "fail(expr + ' executed as: <br/>' + executionOrder + '<br/>expected: '+executionOrder); })";
0162     testCount++;
0163     try {
0164         return eval(functionPrefix + expr + functionPostfix);
0165     } catch(e) {
0166         throw new String(expr);
0167     }
0168 }
0169 
0170 createTestWithExecOrderTest("nonThrowingIndexBase[nonThrowingIndexNoThrowProperty]", "nonThrowingIndexBase, nonThrowingIndexNoThrowProperty, get nonThrowingTestIndex, ")();
0171 
0172 createTestWithExecOrderTest("nonThrowingIndexBase[nonThrowingIndexThrowProperty]", "nonThrowingIndexBase, nonThrowingIndexThrowProperty, get nonThrowingTestIndex, ***throw***")();
0173 
0174 createTestWithExecOrderTest("nonThrowingIndexBase[throwingIndex]", "nonThrowingIndexBase, throwingIndex, ***throw***")();
0175 
0176 createTestWithExecOrderTest("throwingIndexBase[nonThrowingIndexNoThrowProperty]", "throwingIndexBase, ***throw***")();
0177 
0178 createTestWithExecOrderTest("throwingIndexBase[nonThrowingIndexThrowProperty]", "throwingIndexBase, ***throw***")();
0179 
0180 createTestWithExecOrderTest("throwingIndexBase[throwingIndex]", "throwingIndexBase, ***throw***")();
0181 
0182 createTestWithExecOrderTest("nonThrowingIndexBase[nonThrowingIndexNoThrowProperty] = valueForAssignment", "nonThrowingIndexBase, valueForAssignment, nonThrowingIndexNoThrowProperty, set nonThrowingTestIndex, ")();
0183 
0184 createTestWithExecOrderTest("nonThrowingIndexBase[nonThrowingIndexThrowProperty] = valueForAssignment", "nonThrowingIndexBase, valueForAssignment, nonThrowingIndexThrowProperty, set nonThrowingTestIndex, ***throw***")();
0185 
0186 createTestWithExecOrderTest("nonThrowingIndexBase[throwingIndex] = valueForAssignment", "nonThrowingIndexBase, valueForAssignment, throwingIndex, ***throw***")();
0187 
0188 createTestWithExecOrderTest("throwingIndexBase[nonThrowingIndexNoThrowProperty] = valueForAssignment", "throwingIndexBase, ***throw***")();
0189 
0190 createTestWithExecOrderTest("throwingIndexBase[nonThrowingIndexThrowProperty] = valueForAssignment", "throwingIndexBase, ***throw***")();
0191 
0192 createTestWithExecOrderTest("throwingIndexBase[throwingIndex] = valueForAssignment", "throwingIndexBase, ***throw***")();
0193 
0194 
0195 log("Passed " + passCount + " of " + testCount + " tests.");
0196 
0197 //</script>