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>