michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * michael@0: * Date: 15 July 2002 michael@0: * SUMMARY: Testing functions with double-byte names michael@0: * See http://bugzilla.mozilla.org/show_bug.cgi?id=58274 michael@0: * michael@0: * Here is a sample of the problem: michael@0: * michael@0: * js> function f\u02B1 () {} michael@0: * michael@0: * js> f\u02B1.toSource(); michael@0: * function f¦() {} michael@0: * michael@0: * js> f\u02B1.toSource().toSource(); michael@0: * (new String("function f\xB1() {}")) michael@0: * michael@0: * michael@0: * See how the high-byte information (the 02) has been lost? michael@0: * The same thing was happening with the toString() method: michael@0: * michael@0: * js> f\u02B1.toString(); michael@0: * michael@0: * function f¦() { michael@0: * } michael@0: * michael@0: * js> f\u02B1.toString().toSource(); michael@0: * (new String("\nfunction f\xB1() {\n}\n")) michael@0: * michael@0: */ michael@0: //----------------------------------------------------------------------------- michael@0: var UBound = 0; michael@0: var BUGNUMBER = 58274; michael@0: var summary = 'Testing functions with double-byte names'; michael@0: var ERR = 'UNEXPECTED ERROR! \n'; michael@0: var ERR_MALFORMED_NAME = ERR + 'Could not find function name in: \n\n'; michael@0: var status = ''; michael@0: var statusitems = []; michael@0: var actual = ''; michael@0: var actualvalues = []; michael@0: var expect= ''; michael@0: var expectedvalues = []; michael@0: var sEval; michael@0: var sName; michael@0: michael@0: michael@0: sEval = "function f\u02B2() {return 42;}"; michael@0: eval(sEval); michael@0: sName = getFunctionName(f\u02B2); michael@0: michael@0: // Test function call - michael@0: status = inSection(1); michael@0: actual = f\u02B2(); michael@0: expect = 42; michael@0: addThis(); michael@0: michael@0: // Test both characters of function name - michael@0: status = inSection(2); michael@0: actual = sName[0]; michael@0: expect = sEval[9]; michael@0: addThis(); michael@0: michael@0: status = inSection(3); michael@0: actual = sName[1]; michael@0: expect = sEval[10]; michael@0: addThis(); michael@0: michael@0: michael@0: michael@0: sEval = "function f\u02B2\u0AAA () {return 84;}"; michael@0: eval(sEval); michael@0: sName = getFunctionName(f\u02B2\u0AAA); michael@0: michael@0: // Test function call - michael@0: status = inSection(4); michael@0: actual = f\u02B2\u0AAA(); michael@0: expect = 84; michael@0: addThis(); michael@0: michael@0: // Test all three characters of function name - michael@0: status = inSection(5); michael@0: actual = sName[0]; michael@0: expect = sEval[9]; michael@0: addThis(); michael@0: michael@0: status = inSection(6); michael@0: actual = sName[1]; michael@0: expect = sEval[10]; michael@0: addThis(); michael@0: michael@0: status = inSection(7); michael@0: actual = sName[2]; michael@0: expect = sEval[11]; michael@0: addThis(); michael@0: michael@0: michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: test(); michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: michael@0: michael@0: /* michael@0: * Goal: test that f.toString() contains the proper function name. michael@0: * michael@0: * Note, however, f.toString() is implementation-independent. For example, michael@0: * it may begin with '\nfunction' instead of 'function'. Therefore we use michael@0: * a regexp to make sure we extract the name properly. michael@0: * michael@0: * Here we assume that f has been defined by means of a function statement, michael@0: * and not a function expression (where it wouldn't have to have a name). michael@0: * michael@0: * Rhino uses a Unicode representation for f.toString(); whereas michael@0: * SpiderMonkey uses an ASCII representation, putting escape sequences michael@0: * for non-ASCII characters. For example, if a function is called f\u02B1, michael@0: * then in Rhino the toString() method will present a 2-character Unicode michael@0: * string for its name, whereas SpiderMonkey will present a 7-character michael@0: * ASCII string for its name: the string literal 'f\u02B1'. michael@0: * michael@0: * So we force the lexer to condense the string before using it. michael@0: * This will give uniform results in Rhino and SpiderMonkey. michael@0: */ michael@0: function getFunctionName(f) michael@0: { michael@0: var s = condenseStr(f.toString()); michael@0: var re = /\s*function\s+(\S+)\s*\(/; michael@0: var arr = s.match(re); michael@0: michael@0: if (!(arr && arr[1])) michael@0: return ERR_MALFORMED_NAME + s; michael@0: return arr[1]; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * This function is the opposite of functions like escape(), which take michael@0: * Unicode characters and return escape sequences for them. Here, we force michael@0: * the lexer to turn escape sequences back into single characters. michael@0: * michael@0: * Note we can't simply do |eval(str)|, since in practice |str| will be an michael@0: * identifier somewhere in the program (e.g. a function name); thus |eval(str)| michael@0: * would return the object that the identifier represents: not what we want. michael@0: * michael@0: * So we surround |str| lexicographically with quotes to force the lexer to michael@0: * evaluate it as a string. Have to strip out any linefeeds first, however - michael@0: */ michael@0: function condenseStr(str) michael@0: { michael@0: /* michael@0: * You won't be able to do the next step if |str| has michael@0: * any carriage returns or linefeeds in it. For example: michael@0: * michael@0: * js> eval("'" + '\nHello' + "'"); michael@0: * 1: SyntaxError: unterminated string literal: michael@0: * 1: ' michael@0: * 1: ^ michael@0: * michael@0: * So replace them with the empty string - michael@0: */ michael@0: str = str.replace(/[\r\n]/g, '') michael@0: return eval("'" + str + "'"); michael@0: } michael@0: michael@0: michael@0: function addThis() michael@0: { michael@0: statusitems[UBound] = status; michael@0: actualvalues[UBound] = actual; michael@0: expectedvalues[UBound] = expect; michael@0: UBound++; michael@0: } michael@0: michael@0: michael@0: function test() michael@0: { michael@0: enterFunc('test'); michael@0: printBugNumber(BUGNUMBER); michael@0: printStatus(summary); michael@0: michael@0: for (var i=0; i