1/* ***** BEGIN LICENSE BLOCK ***** 2* Version: NPL 1.1/GPL 2.0/LGPL 2.1 3* 4* The contents of this file are subject to the Netscape Public License 5* Version 1.1 (the "License"); you may not use this file except in 6* compliance with the License. You may obtain a copy of the License at 7* http://www.mozilla.org/NPL/ 8* 9* Software distributed under the License is distributed on an "AS IS" basis, 10* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11* for the specific language governing rights and limitations under the 12* License. 13* 14* The Original Code is JavaScript Engine testing utilities. 15* 16* The Initial Developer of the Original Code is Netscape Communications Corp. 17* Portions created by the Initial Developer are Copyright (C) 2002 18* the Initial Developer. All Rights Reserved. 19* 20* Contributor(s): rogerl@netscape.com, pschwartau@netscape.com 21* 22* Alternatively, the contents of this file may be used under the terms of 23* either the GNU General Public License Version 2 or later (the "GPL"), or 24* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 25* in which case the provisions of the GPL or the LGPL are applicable instead 26* of those above. If you wish to allow use of your version of this file only 27* under the terms of either the GPL or the LGPL, and not to allow others to 28* use your version of this file under the terms of the NPL, indicate your 29* decision by deleting the provisions above and replace them with the notice 30* and other provisions required by the GPL or the LGPL. If you do not delete 31* the provisions above, a recipient may use your version of this file under 32* the terms of any one of the NPL, the GPL or the LGPL. 33* 34* ***** END LICENSE BLOCK ***** 35* 36* 37* Date: 15 July 2002 38* SUMMARY: Testing identifiers with double-byte names 39* See http://bugzilla.mozilla.org/show_bug.cgi?id=58274 40* 41* Here is a sample of the problem: 42* 43* js> function f\u02B1 () {} 44* 45* js> f\u02B1.toSource(); 46* function f¦() {} 47* 48* js> f\u02B1.toSource().toSource(); 49* (new String("function f\xB1() {}")) 50* 51* 52* See how the high-byte information (the 02) has been lost? 53* The same thing was happening with the toString() method: 54* 55* js> f\u02B1.toString(); 56* 57* function f¦() { 58* } 59* 60* js> f\u02B1.toString().toSource(); 61* (new String("\nfunction f\xB1() {\n}\n")) 62* 63*/ 64//----------------------------------------------------------------------------- 65var UBound = 0; 66var bug = 58274; 67var summary = 'Testing identifiers with double-byte names'; 68var status = ''; 69var statusitems = []; 70var actual = ''; 71var actualvalues = []; 72var expect= ''; 73var expectedvalues = []; 74 75 76/* 77 * Define a function that uses double-byte identifiers in 78 * "every possible way" 79 * 80 * Then recover each double-byte identifier via f.toString(). 81 * To make this easier, put a 'Z' token before every one. 82 * 83 * Our eval string will be: 84 * 85 * sEval = "function Z\u02b1(Z\u02b2, b) { 86 * try { Z\u02b3 : var Z\u02b4 = Z\u02b1; } 87 * catch (Z\u02b5) { for (var Z\u02b6 in Z\u02b5) 88 * {for (1; 1<0; Z\u02b7++) {new Array()[Z\u02b6] = 1;} };} }"; 89 * 90 * It will be helpful to build this string in stages: 91 */ 92var s0 = 'function Z'; 93var s1 = '\u02b1(Z'; 94var s2 = '\u02b2, b) {try { Z'; 95var s3 = '\u02b3 : var Z'; 96var s4 = '\u02b4 = Z'; 97var s5 = '\u02b1; } catch (Z' 98var s6 = '\u02b5) { for (var Z'; 99var s7 = '\u02b6 in Z'; 100var s8 = '\u02b5){for (1; 1<0; Z'; 101var s9 = '\u02b7++) {new Array()[Z'; 102var s10 = '\u02b6] = 1;} };} }'; 103 104 105/* 106 * Concatenate these and eval() to create the function Z\u02b1 107 */ 108var sEval = s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10; 109eval(sEval); 110 111 112/* 113 * Recover all the double-byte identifiers via Z\u02b1.toString(). 114 * We'll recover the 1st one as arrID[1], the 2nd one as arrID[2], 115 * and so on ... 116 */ 117var arrID = getIdentifiers(Z\u02b1); 118 119 120/* 121 * Now check that we got back what we put in - 122 */ 123status = inSection(1); 124actual = arrID[1]; 125expect = s1.charAt(0); 126addThis(); 127 128status = inSection(2); 129actual = arrID[2]; 130expect = s2.charAt(0); 131addThis(); 132 133status = inSection(3); 134actual = arrID[3]; 135expect = s3.charAt(0); 136addThis(); 137 138status = inSection(4); 139actual = arrID[4]; 140expect = s4.charAt(0); 141addThis(); 142 143status = inSection(5); 144actual = arrID[5]; 145expect = s5.charAt(0); 146addThis(); 147 148status = inSection(6); 149actual = arrID[6]; 150expect = s6.charAt(0); 151addThis(); 152 153status = inSection(7); 154actual = arrID[7]; 155expect = s7.charAt(0); 156addThis(); 157 158status = inSection(8); 159actual = arrID[8]; 160expect = s8.charAt(0); 161addThis(); 162 163status = inSection(9); 164actual = arrID[9]; 165expect = s9.charAt(0); 166addThis(); 167 168status = inSection(10); 169actual = arrID[10]; 170expect = s10.charAt(0); 171addThis(); 172 173 174 175 176//----------------------------------------------------------------------------- 177test(); 178//----------------------------------------------------------------------------- 179 180 181 182/* 183 * Goal: recover the double-byte identifiers from f.toString() 184 * by getting the very next character after each 'Z' token. 185 * 186 * The return value will be an array |arr| indexed such that 187 * |arr[1]| is the 1st identifier, |arr[2]| the 2nd, and so on. 188 * 189 * Note, however, f.toString() is implementation-independent. 190 * For example, it may begin with '\nfunction' instead of 'function'. 191 * 192 * Rhino uses a Unicode representation for f.toString(); whereas 193 * SpiderMonkey uses an ASCII representation, putting escape sequences 194 * for non-ASCII characters. For example, if a function is called f\u02B1, 195 * then in Rhino the toString() method will present a 2-character Unicode 196 * string for its name, whereas SpiderMonkey will present a 7-character 197 * ASCII string for its name: the string literal 'f\u02B1'. 198 * 199 * So we force the lexer to condense the string before we use it. 200 * This will give uniform results in Rhino and SpiderMonkey. 201 */ 202function getIdentifiers(f) 203{ 204 var str = condenseStr(f.toString()); 205 var arr = str.split('Z'); 206 207 /* 208 * The identifiers are the 1st char of each split substring 209 * EXCEPT the first one, which is just ('\n' +) 'function '. 210 * 211 * Thus note the 1st identifier will be stored in |arr[1]|, 212 * the 2nd one in |arr[2]|, etc., making the indexing easy - 213 */ 214 for (i in arr) 215 arr[i] = arr[i].charAt(0); 216 return arr; 217} 218 219 220/* 221 * This function is the opposite of a functions like escape(), which take 222 * Unicode characters and return escape sequences for them. Here, we force 223 * the lexer to turn escape sequences back into single characters. 224 * 225 * Note we can't simply do |eval(str)|, since in practice |str| will be an 226 * identifier somewhere in the program (e.g. a function name); thus |eval(str)| 227 * would return the object that the identifier represents: not what we want. 228 * 229 * So we surround |str| lexicographically with quotes to force the lexer to 230 * evaluate it as a string. Have to strip out any linefeeds first, however - 231 */ 232function condenseStr(str) 233{ 234 /* 235 * You won't be able to do the next step if |str| has 236 * any carriage returns or linefeeds in it. For example: 237 * 238 * js> eval("'" + '\nHello' + "'"); 239 * 1: SyntaxError: unterminated string literal: 240 * 1: ' 241 * 1: ^ 242 * 243 * So replace them with the empty string - 244 */ 245 str = str.replace(/[\r\n]/g, '') 246 return eval("'" + str + "'") 247} 248 249 250function addThis() 251{ 252 statusitems[UBound] = status; 253 actualvalues[UBound] = actual; 254 expectedvalues[UBound] = expect; 255 UBound++; 256} 257 258 259function test() 260{ 261 enterFunc('test'); 262 printBugNumber(bug); 263 printStatus(summary); 264 265 for (var i=0; i<UBound; i++) 266 { 267 reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); 268 } 269 270 exitFunc ('test'); 271} 272