1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7// This file relies on the fact that the following declaration has been made
8// in runtime.js:
9// var $String = global.String;
10// var $Array = global.Array;
11
12// -------------------------------------------------------------------
13
14// ES6 draft 01-20-14, section 21.1.3.13
15function StringRepeat(count) {
16  CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
17
18  var s = TO_STRING_INLINE(this);
19  var n = ToInteger(count);
20  if (n < 0 || !NUMBER_IS_FINITE(n)) {
21    throw MakeRangeError("invalid_count_value", []);
22  }
23
24  var elements = new InternalArray(n);
25  for (var i = 0; i < n; i++) {
26    elements[i] = s;
27  }
28
29  return %StringBuilderConcat(elements, n, "");
30}
31
32
33// ES6 draft 04-05-14, section 21.1.3.18
34function StringStartsWith(searchString /* position */) {  // length == 1
35  CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
36
37  var s = TO_STRING_INLINE(this);
38
39  if (IS_REGEXP(searchString)) {
40    throw MakeTypeError("first_argument_not_regexp",
41                        ["String.prototype.startsWith"]);
42  }
43
44  var ss = TO_STRING_INLINE(searchString);
45  var pos = 0;
46  if (%_ArgumentsLength() > 1) {
47    pos = %_Arguments(1);  // position
48    pos = ToInteger(pos);
49  }
50
51  var s_len = s.length;
52  var start = MathMin(MathMax(pos, 0), s_len);
53  var ss_len = ss.length;
54  if (ss_len + start > s_len) {
55    return false;
56  }
57
58  return %StringIndexOf(s, ss, start) === start;
59}
60
61
62// ES6 draft 04-05-14, section 21.1.3.7
63function StringEndsWith(searchString /* position */) {  // length == 1
64  CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
65
66  var s = TO_STRING_INLINE(this);
67
68  if (IS_REGEXP(searchString)) {
69    throw MakeTypeError("first_argument_not_regexp",
70                        ["String.prototype.endsWith"]);
71  }
72
73  var ss = TO_STRING_INLINE(searchString);
74  var s_len = s.length;
75  var pos = s_len;
76  if (%_ArgumentsLength() > 1) {
77    var arg = %_Arguments(1);  // position
78    if (!IS_UNDEFINED(arg)) {
79      pos = ToInteger(arg);
80    }
81  }
82
83  var end = MathMin(MathMax(pos, 0), s_len);
84  var ss_len = ss.length;
85  var start = end - ss_len;
86  if (start < 0) {
87    return false;
88  }
89
90  return %StringLastIndexOf(s, ss, start) === start;
91}
92
93
94// ES6 draft 04-05-14, section 21.1.3.6
95function StringContains(searchString /* position */) {  // length == 1
96  CHECK_OBJECT_COERCIBLE(this, "String.prototype.contains");
97
98  var s = TO_STRING_INLINE(this);
99
100  if (IS_REGEXP(searchString)) {
101    throw MakeTypeError("first_argument_not_regexp",
102                        ["String.prototype.contains"]);
103  }
104
105  var ss = TO_STRING_INLINE(searchString);
106  var pos = 0;
107  if (%_ArgumentsLength() > 1) {
108    pos = %_Arguments(1);  // position
109    pos = ToInteger(pos);
110  }
111
112  var s_len = s.length;
113  var start = MathMin(MathMax(pos, 0), s_len);
114  var ss_len = ss.length;
115  if (ss_len + start > s_len) {
116    return false;
117  }
118
119  return %StringIndexOf(s, ss, start) !== -1;
120}
121
122
123// ES6 Draft 05-22-2014, section 21.1.3.3
124function StringCodePointAt(pos) {
125  CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
126
127  var string = TO_STRING_INLINE(this);
128  var size = string.length;
129  pos = TO_INTEGER(pos);
130  if (pos < 0 || pos >= size) {
131    return UNDEFINED;
132  }
133  var first = %_StringCharCodeAt(string, pos);
134  if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
135    return first;
136  }
137  var second = %_StringCharCodeAt(string, pos + 1);
138  if (second < 0xDC00 || second > 0xDFFF) {
139    return first;
140  }
141  return (first - 0xD800) * 0x400 + second + 0x2400;
142}
143
144
145// ES6 Draft 05-22-2014, section 21.1.2.2
146function StringFromCodePoint(_) {  // length = 1
147  var code;
148  var length = %_ArgumentsLength();
149  var index;
150  var result = "";
151  for (index = 0; index < length; index++) {
152    code = %_Arguments(index);
153    if (!%_IsSmi(code)) {
154      code = ToNumber(code);
155    }
156    if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
157      throw MakeRangeError("invalid_code_point", [code]);
158    }
159    if (code <= 0xFFFF) {
160      result += %_StringCharFromCode(code);
161    } else {
162      code -= 0x10000;
163      result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
164      result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
165    }
166  }
167  return result;
168}
169
170
171// -------------------------------------------------------------------
172
173function ExtendStringPrototype() {
174  %CheckIsBootstrapping();
175
176  // Set up the non-enumerable functions on the String object.
177  InstallFunctions($String, DONT_ENUM, $Array(
178    "fromCodePoint", StringFromCodePoint
179  ));
180
181  // Set up the non-enumerable functions on the String prototype object.
182  InstallFunctions($String.prototype, DONT_ENUM, $Array(
183    "codePointAt", StringCodePointAt,
184    "contains", StringContains,
185    "endsWith", StringEndsWith,
186    "repeat", StringRepeat,
187    "startsWith", StringStartsWith
188  ));
189}
190
191ExtendStringPrototype();
192