// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var s = "test";
function getTwoByteString() { return "\u1234t"; }
function getCons() { return "testtesttesttest" + getTwoByteString() }
var slowIndex1 = { valueOf: function() { return 1; } };
var slowIndex2 = { toString: function() { return "2"; } };
var slowIndexOutOfRange = { valueOf: function() { return -1; } };
function basicTest(s, len) {
assertEquals("t", s().charAt());
assertEquals("t", s().charAt("string"));
assertEquals("t", s().charAt(null));
assertEquals("t", s().charAt(void 0));
assertEquals("t", s().charAt(false));
assertEquals("e", s().charAt(true));
assertEquals("", s().charAt(-1));
assertEquals("", s().charAt(len));
assertEquals("", s().charAt(slowIndexOutOfRange));
assertEquals("", s().charAt(1/0));
assertEquals("", s().charAt(-1/0));
assertEquals("t", s().charAt(0));
assertEquals("t", s().charAt(-0.0));
assertEquals("t", s().charAt(-0.1));
assertEquals("t", s().charAt(0.4));
assertEquals("e", s().charAt(slowIndex1));
assertEquals("s", s().charAt(slowIndex2));
assertEquals("t", s().charAt(3));
assertEquals("t", s().charAt(3.4));
assertEquals("t", s().charAt(NaN));
assertEquals(116, s().charCodeAt());
assertEquals(116, s().charCodeAt("string"));
assertEquals(116, s().charCodeAt(null));
assertEquals(116, s().charCodeAt(void 0));
assertEquals(116, s().charCodeAt(false));
assertEquals(101, s().charCodeAt(true));
assertEquals(116, s().charCodeAt(0));
assertEquals(116, s().charCodeAt(-0.0));
assertEquals(116, s().charCodeAt(-0.1));
assertEquals(116, s().charCodeAt(0.4));
assertEquals(101, s().charCodeAt(slowIndex1));
assertEquals(115, s().charCodeAt(slowIndex2));
assertEquals(116, s().charCodeAt(3));
assertEquals(116, s().charCodeAt(3.4));
assertEquals(116, s().charCodeAt(NaN));
assertTrue(isNaN(s().charCodeAt(-1)));
assertTrue(isNaN(s().charCodeAt(len)));
assertTrue(isNaN(s().charCodeAt(slowIndexOutOfRange)));
assertTrue(isNaN(s().charCodeAt(1/0)));
assertTrue(isNaN(s().charCodeAt(-1/0)));
}
basicTest(function() { return s; }, s.length);
basicTest(getCons, getCons().length);
// Make sure enough of the one-char string cache is filled.
var alpha = ['@'];
for (var i = 1; i < 128; i++) {
var c = String.fromCharCode(i);
alpha[i] = c.charAt(0);
}
var alphaStr = alpha.join("");
// Now test chars.
for (var i = 1; i < 128; i++) {
assertEquals(alpha[i], alphaStr.charAt(i));
assertEquals(String.fromCharCode(i), alphaStr.charAt(i));
}
// Test stealing String.prototype.{charAt,charCodeAt}.
var o = {
charAt: String.prototype.charAt,
charCodeAt: String.prototype.charCodeAt,
toString: function() { return "012"; },
valueOf: function() { return "should not be called"; }
};
function stealTest() {
assertEquals("0", o.charAt(0));
assertEquals("1", o.charAt(1));
assertEquals("1", o.charAt(1.4));
assertEquals("1", o.charAt(slowIndex1));
assertEquals("2", o.charAt(2));
assertEquals("2", o.charAt(slowIndex2));
assertEquals(48, o.charCodeAt(0));
assertEquals(49, o.charCodeAt(1));
assertEquals(49, o.charCodeAt(1.4));
assertEquals(49, o.charCodeAt(slowIndex1));
assertEquals(50, o.charCodeAt(2));
assertEquals(50, o.charCodeAt(slowIndex2));
assertEquals("", o.charAt(-1));
assertEquals("", o.charAt(-1.4));
assertEquals("", o.charAt(10));
assertEquals("", o.charAt(slowIndexOutOfRange));
assertTrue(isNaN(o.charCodeAt(-1)));
assertTrue(isNaN(o.charCodeAt(-1.4)));
assertTrue(isNaN(o.charCodeAt(10)));
assertTrue(isNaN(o.charCodeAt(slowIndexOutOfRange)));
}
stealTest();
// Test custom string IC-s.
for (var i = 0; i < 20; i++) {
basicTest(function() { return s; }, s.length);
basicTest(getCons, getCons().length);
stealTest();
}
var badToString = function() { return []; };
function testBadToString_charAt() {
var goodToString = o.toString;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) o.toString = o.valueOf = badToString;
result = o.charAt(1);
numCalls++;
}
} catch (e) {
hasCaught = true;
} finally {
o.toString = goodToString;
}
assertTrue(hasCaught);
assertEquals("1", result);
assertEquals(10, numCalls);
}
testBadToString_charAt();
function testBadToString_charCodeAt() {
var goodToString = o.toString;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) o.toString = o.valueOf = badToString;
result = o.charCodeAt(1);
numCalls++;
}
} catch (e) {
hasCaught = true;
} finally {
o.toString = goodToString;
}
assertTrue(hasCaught);
assertEquals(49, result);
assertEquals(10, numCalls);
}
testBadToString_charCodeAt();
var badIndex = {
toString: badToString,
valueOf: badToString
};
function testBadIndex_charAt() {
var index = 1;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) index = badIndex;
result = o.charAt(index);
numCalls++;
}
} catch (e) {
hasCaught = true;
}
assertTrue(hasCaught);
assertEquals("1", result);
assertEquals(10, numCalls);
}
testBadIndex_charAt();
function testBadIndex_charCodeAt() {
var index = 1;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) index = badIndex;
result = o.charCodeAt(index);
numCalls++;
}
} catch (e) {
hasCaught = true;
}
assertTrue(hasCaught);
assertEquals(49, result);
assertEquals(10, numCalls);
}
testBadIndex_charCodeAt();
function testPrototypeChange_charAt() {
var result, oldResult;
for (var i = 0; i < 20; i++) {
if (i == 10) {
oldResult = result;
String.prototype.charAt = function() { return "%"; };
}
result = s.charAt(1);
}
assertEquals("%", result);
assertEquals("e", oldResult);
delete String.prototype.charAt; // Restore the default.
}
testPrototypeChange_charAt();
function testPrototypeChange_charCodeAt() {
var result, oldResult;
for (var i = 0; i < 20; i++) {
if (i == 10) {
oldResult = result;
String.prototype.charCodeAt = function() { return 42; };
}
result = s.charCodeAt(1);
}
assertEquals(42, result);
assertEquals(101, oldResult);
delete String.prototype.charCodeAt; // Restore the default.
}
testPrototypeChange_charCodeAt();