1// Copyright 2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28var s = "test";
29
30function getTwoByteString() { return "\u1234t"; }
31function getCons() { return "testtesttesttest" + getTwoByteString() }
32
33var slowIndex1 = { valueOf: function() { return 1; } };
34var slowIndex2 = { toString: function() { return "2"; } };
35var slowIndexOutOfRange = { valueOf: function() { return -1; } };
36
37function basicTest(s, len) {
38  assertEquals("t", s().charAt());
39  assertEquals("t", s().charAt("string"));
40  assertEquals("t", s().charAt(null));
41  assertEquals("t", s().charAt(void 0));
42  assertEquals("t", s().charAt(false));
43  assertEquals("e", s().charAt(true));
44  assertEquals("", s().charAt(-1));
45  assertEquals("", s().charAt(len));
46  assertEquals("", s().charAt(slowIndexOutOfRange));
47  assertEquals("", s().charAt(1/0));
48  assertEquals("", s().charAt(-1/0));
49  assertEquals("t", s().charAt(0));
50  assertEquals("t", s().charAt(-0.0));
51  assertEquals("t", s().charAt(-0.1));
52  assertEquals("t", s().charAt(0.4));
53  assertEquals("e", s().charAt(slowIndex1));
54  assertEquals("s", s().charAt(slowIndex2));
55  assertEquals("t", s().charAt(3));
56  assertEquals("t", s().charAt(3.4));
57  assertEquals("t", s().charAt(NaN));
58
59  assertEquals(116, s().charCodeAt());
60  assertEquals(116, s().charCodeAt("string"));
61  assertEquals(116, s().charCodeAt(null));
62  assertEquals(116, s().charCodeAt(void 0));
63  assertEquals(116, s().charCodeAt(false));
64  assertEquals(101, s().charCodeAt(true));
65  assertEquals(116, s().charCodeAt(0));
66  assertEquals(116, s().charCodeAt(-0.0));
67  assertEquals(116, s().charCodeAt(-0.1));
68  assertEquals(116, s().charCodeAt(0.4));
69  assertEquals(101, s().charCodeAt(slowIndex1));
70  assertEquals(115, s().charCodeAt(slowIndex2));
71  assertEquals(116, s().charCodeAt(3));
72  assertEquals(116, s().charCodeAt(3.4));
73  assertEquals(116, s().charCodeAt(NaN));
74  assertTrue(isNaN(s().charCodeAt(-1)));
75  assertTrue(isNaN(s().charCodeAt(len)));
76  assertTrue(isNaN(s().charCodeAt(slowIndexOutOfRange)));
77  assertTrue(isNaN(s().charCodeAt(1/0)));
78  assertTrue(isNaN(s().charCodeAt(-1/0)));
79}
80basicTest(function() { return s; }, s.length);
81basicTest(getCons, getCons().length);
82
83// Make sure enough of the one-char string cache is filled.
84var alpha = ['@'];
85for (var i = 1; i < 128; i++) {
86  var c = String.fromCharCode(i);
87  alpha[i] = c.charAt(0);
88}
89var alphaStr = alpha.join("");
90
91// Now test chars.
92for (var i = 1; i < 128; i++) {
93  assertEquals(alpha[i], alphaStr.charAt(i));
94  assertEquals(String.fromCharCode(i), alphaStr.charAt(i));
95}
96
97// Test stealing String.prototype.{charAt,charCodeAt}.
98var o = {
99  charAt: String.prototype.charAt,
100  charCodeAt: String.prototype.charCodeAt,
101  toString: function() { return "012"; },
102  valueOf: function() { return "should not be called"; }
103};
104
105function stealTest() {
106  assertEquals("0", o.charAt(0));
107  assertEquals("1", o.charAt(1));
108  assertEquals("1", o.charAt(1.4));
109  assertEquals("1", o.charAt(slowIndex1));
110  assertEquals("2", o.charAt(2));
111  assertEquals("2", o.charAt(slowIndex2));
112  assertEquals(48, o.charCodeAt(0));
113  assertEquals(49, o.charCodeAt(1));
114  assertEquals(49, o.charCodeAt(1.4));
115  assertEquals(49, o.charCodeAt(slowIndex1));
116  assertEquals(50, o.charCodeAt(2));
117  assertEquals(50, o.charCodeAt(slowIndex2));
118  assertEquals("", o.charAt(-1));
119  assertEquals("", o.charAt(-1.4));
120  assertEquals("", o.charAt(10));
121  assertEquals("", o.charAt(slowIndexOutOfRange));
122  assertTrue(isNaN(o.charCodeAt(-1)));
123  assertTrue(isNaN(o.charCodeAt(-1.4)));
124  assertTrue(isNaN(o.charCodeAt(10)));
125  assertTrue(isNaN(o.charCodeAt(slowIndexOutOfRange)));
126}
127stealTest();
128
129// Test custom string IC-s.
130for (var i = 0; i < 20; i++) {
131  basicTest(function() { return s; }, s.length);
132  basicTest(getCons, getCons().length);
133  stealTest();
134}
135
136var badToString = function() { return []; };
137
138function testBadToString_charAt() {
139  var goodToString = o.toString;
140  var hasCaught = false;
141  var numCalls = 0;
142  var result;
143  try {
144    for (var i = 0; i < 20; i++) {
145      if (i == 10) o.toString = o.valueOf = badToString;
146      result = o.charAt(1);
147      numCalls++;
148    }
149  } catch (e) {
150    hasCaught = true;
151  } finally {
152    o.toString = goodToString;
153  }
154  assertTrue(hasCaught);
155  assertEquals("1", result);
156  assertEquals(10, numCalls);
157}
158testBadToString_charAt();
159
160function testBadToString_charCodeAt() {
161  var goodToString = o.toString;
162  var hasCaught = false;
163  var numCalls = 0;
164  var result;
165  try {
166    for (var i = 0; i < 20; i++) {
167      if (i == 10) o.toString = o.valueOf = badToString;
168      result = o.charCodeAt(1);
169      numCalls++;
170    }
171  } catch (e) {
172    hasCaught = true;
173  } finally {
174    o.toString = goodToString;
175  }
176  assertTrue(hasCaught);
177  assertEquals(49, result);
178  assertEquals(10, numCalls);
179}
180testBadToString_charCodeAt();
181
182var badIndex = {
183  toString: badToString,
184  valueOf: badToString
185};
186
187function testBadIndex_charAt() {
188  var index = 1;
189  var hasCaught = false;
190  var numCalls = 0;
191  var result;
192  try {
193    for (var i = 0; i < 20; i++) {
194      if (i == 10) index = badIndex;
195      result = o.charAt(index);
196      numCalls++;
197    }
198  } catch (e) {
199    hasCaught = true;
200  }
201  assertTrue(hasCaught);
202  assertEquals("1", result);
203  assertEquals(10, numCalls);
204}
205testBadIndex_charAt();
206
207function testBadIndex_charCodeAt() {
208  var index = 1;
209  var hasCaught = false;
210  var numCalls = 0;
211  var result;
212  try {
213    for (var i = 0; i < 20; i++) {
214      if (i == 10) index = badIndex;
215      result = o.charCodeAt(index);
216      numCalls++;
217    }
218  } catch (e) {
219    hasCaught = true;
220  }
221  assertTrue(hasCaught);
222  assertEquals(49, result);
223  assertEquals(10, numCalls);
224}
225testBadIndex_charCodeAt();
226
227function testPrototypeChange_charAt() {
228  var result, oldResult;
229  for (var i = 0; i < 20; i++) {
230    if (i == 10) {
231      oldResult = result;
232      String.prototype.charAt = function() { return "%"; };
233    }
234    result = s.charAt(1);
235  }
236  assertEquals("%", result);
237  assertEquals("e", oldResult);
238  delete String.prototype.charAt;  // Restore the default.
239}
240testPrototypeChange_charAt();
241
242function testPrototypeChange_charCodeAt() {
243  var result, oldResult;
244  for (var i = 0; i < 20; i++) {
245    if (i == 10) {
246      oldResult = result;
247      String.prototype.charCodeAt = function() { return 42; };
248    }
249    result = s.charCodeAt(1);
250  }
251  assertEquals(42, result);
252  assertEquals(101, oldResult);
253  delete String.prototype.charCodeAt;  // Restore the default.
254}
255testPrototypeChange_charCodeAt();
256