Line data Source code
1 : // Copyright 2016 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 : #include "src/builtins/builtins-utils.h"
6 : #include "src/builtins/builtins.h"
7 : #include "src/conversions.h"
8 : #include "src/counters.h"
9 : #include "src/objects-inl.h"
10 : #include "src/regexp/regexp-utils.h"
11 : #include "src/string-case.h"
12 : #include "src/unicode-inl.h"
13 : #include "src/unicode.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : namespace { // for String.fromCodePoint
19 :
20 16974232 : bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
21 16974232 : if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
22 : return false;
23 : }
24 :
25 16974232 : if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
26 : value->Number()) {
27 : return false;
28 : }
29 :
30 33948211 : if (value->Number() < 0 || value->Number() > 0x10FFFF) {
31 : return false;
32 : }
33 :
34 16974020 : return true;
35 : }
36 :
37 16974260 : uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
38 16974260 : Handle<Object> value = args.at(1 + index);
39 33948520 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
40 16974232 : if (!IsValidCodePoint(isolate, value)) {
41 : isolate->Throw(*isolate->factory()->NewRangeError(
42 424 : MessageTemplate::kInvalidCodePoint, value));
43 : return -1;
44 : }
45 : return DoubleToUint32(value->Number());
46 : }
47 :
48 : } // namespace
49 :
50 : // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
51 46793970 : BUILTIN(StringFromCodePoint) {
52 : HandleScope scope(isolate);
53 15597990 : int const length = args.length() - 1;
54 15597990 : if (length == 0) return isolate->heap()->empty_string();
55 : DCHECK_LT(0, length);
56 :
57 : // Optimistically assume that the resulting String contains only one byte
58 : // characters.
59 : List<uint8_t> one_byte_buffer(length);
60 : uc32 code = 0;
61 : int index;
62 16289814 : for (index = 0; index < length; index++) {
63 16286118 : code = NextCodePoint(isolate, args, index);
64 16286118 : if (code < 0) {
65 240 : return isolate->heap()->exception();
66 : }
67 16285878 : if (code > String::kMaxOneByteCharCode) {
68 : break;
69 : }
70 691838 : one_byte_buffer.Add(code);
71 : }
72 :
73 15597736 : if (index == length) {
74 11088 : RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte(
75 : one_byte_buffer.ToConstVector()));
76 : }
77 :
78 15594040 : List<uc16> two_byte_buffer(length - index);
79 :
80 : while (true) {
81 16282182 : if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
82 913934 : two_byte_buffer.Add(code);
83 : } else {
84 30736496 : two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code));
85 15368248 : two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code));
86 : }
87 :
88 16282182 : if (++index == length) {
89 : break;
90 : }
91 688142 : code = NextCodePoint(isolate, args, index);
92 688142 : if (code < 0) {
93 0 : return isolate->heap()->exception();
94 : }
95 : }
96 :
97 : Handle<SeqTwoByteString> result;
98 31188080 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
99 : isolate, result,
100 : isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() +
101 : two_byte_buffer.length()));
102 :
103 15594040 : CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
104 31188080 : one_byte_buffer.length());
105 : CopyChars(result->GetChars() + one_byte_buffer.length(),
106 15594040 : two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
107 :
108 15594040 : return *result;
109 : }
110 :
111 : // ES6 section 21.1.3.6
112 : // String.prototype.endsWith ( searchString [ , endPosition ] )
113 11511 : BUILTIN(StringPrototypeEndsWith) {
114 : HandleScope handle_scope(isolate);
115 8178 : TO_THIS_STRING(str, "String.prototype.endsWith");
116 :
117 : // Check if the search string is a regExp and fail if it is.
118 3557 : Handle<Object> search = args.atOrUndefined(isolate, 1);
119 3557 : Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
120 3557 : if (is_reg_exp.IsNothing()) {
121 : DCHECK(isolate->has_pending_exception());
122 0 : return isolate->heap()->exception();
123 : }
124 3557 : if (is_reg_exp.FromJust()) {
125 210 : THROW_NEW_ERROR_RETURN_FAILURE(
126 : isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
127 : isolate->factory()->NewStringFromStaticChars(
128 : "String.prototype.endsWith")));
129 : }
130 : Handle<String> search_string;
131 6974 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
132 : Object::ToString(isolate, search));
133 :
134 : Handle<Object> position = args.atOrUndefined(isolate, 2);
135 : int end;
136 :
137 3487 : if (position->IsUndefined(isolate)) {
138 : end = str->length();
139 : } else {
140 2142 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
141 : Object::ToInteger(isolate, position));
142 2142 : end = str->ToValidIndex(*position);
143 : }
144 :
145 3487 : int start = end - search_string->length();
146 3487 : if (start < 0) return isolate->heap()->false_value();
147 :
148 2003 : str = String::Flatten(str);
149 2003 : search_string = String::Flatten(search_string);
150 :
151 : DisallowHeapAllocation no_gc; // ensure vectors stay valid
152 2003 : String::FlatContent str_content = str->GetFlatContent();
153 2003 : String::FlatContent search_content = search_string->GetFlatContent();
154 :
155 2003 : if (str_content.IsOneByte() && search_content.IsOneByte()) {
156 1751 : Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
157 1751 : Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
158 :
159 1751 : return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
160 : search_vector.start(),
161 3502 : search_string->length()) == 0);
162 : }
163 :
164 252 : FlatStringReader str_reader(isolate, str);
165 252 : FlatStringReader search_reader(isolate, search_string);
166 :
167 1736 : for (int i = 0; i < search_string->length(); i++) {
168 1344 : if (str_reader.Get(start + i) != search_reader.Get(i)) {
169 56 : return isolate->heap()->false_value();
170 : }
171 : }
172 196 : return isolate->heap()->true_value();
173 : }
174 :
175 : // ES6 section 21.1.3.7
176 : // String.prototype.includes ( searchString [ , position ] )
177 6177 : BUILTIN(StringPrototypeIncludes) {
178 : HandleScope handle_scope(isolate);
179 4286 : TO_THIS_STRING(str, "String.prototype.includes");
180 :
181 : // Check if the search string is a regExp and fail if it is.
182 1947 : Handle<Object> search = args.atOrUndefined(isolate, 1);
183 1947 : Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
184 1947 : if (is_reg_exp.IsNothing()) {
185 : DCHECK(isolate->has_pending_exception());
186 0 : return isolate->heap()->exception();
187 : }
188 1947 : if (is_reg_exp.FromJust()) {
189 294 : THROW_NEW_ERROR_RETURN_FAILURE(
190 : isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
191 : isolate->factory()->NewStringFromStaticChars(
192 : "String.prototype.includes")));
193 : }
194 : Handle<String> search_string;
195 3698 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
196 : Object::ToString(isolate, search));
197 : Handle<Object> position;
198 1849 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
199 : isolate, position,
200 : Object::ToInteger(isolate, args.atOrUndefined(isolate, 2)));
201 :
202 : uint32_t index = str->ToValidIndex(*position);
203 1849 : int index_in_str = String::IndexOf(isolate, str, search_string, index);
204 3698 : return *isolate->factory()->ToBoolean(index_in_str != -1);
205 : }
206 :
207 : // ES6 section 21.1.3.9
208 : // String.prototype.lastIndexOf ( searchString [ , position ] )
209 8223 : BUILTIN(StringPrototypeLastIndexOf) {
210 : HandleScope handle_scope(isolate);
211 : return String::LastIndexOf(isolate, args.receiver(),
212 : args.atOrUndefined(isolate, 1),
213 5482 : args.atOrUndefined(isolate, 2));
214 : }
215 :
216 : // ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
217 : //
218 : // This function is implementation specific. For now, we do not
219 : // do anything locale specific.
220 : // If internationalization is enabled, then intl.js will override this function
221 : // and provide the proper functionality, so this is just a fallback.
222 0 : BUILTIN(StringPrototypeLocaleCompare) {
223 : HandleScope handle_scope(isolate);
224 : DCHECK_EQ(2, args.length());
225 :
226 0 : TO_THIS_STRING(str1, "String.prototype.localeCompare");
227 : Handle<String> str2;
228 0 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2,
229 : Object::ToString(isolate, args.at(1)));
230 :
231 0 : if (str1.is_identical_to(str2)) return Smi::kZero; // Equal.
232 : int str1_length = str1->length();
233 : int str2_length = str2->length();
234 :
235 : // Decide trivial cases without flattening.
236 0 : if (str1_length == 0) {
237 0 : if (str2_length == 0) return Smi::kZero; // Equal.
238 0 : return Smi::FromInt(-str2_length);
239 : } else {
240 0 : if (str2_length == 0) return Smi::FromInt(str1_length);
241 : }
242 :
243 0 : int end = str1_length < str2_length ? str1_length : str2_length;
244 :
245 : // No need to flatten if we are going to find the answer on the first
246 : // character. At this point we know there is at least one character
247 : // in each string, due to the trivial case handling above.
248 0 : int d = str1->Get(0) - str2->Get(0);
249 0 : if (d != 0) return Smi::FromInt(d);
250 :
251 0 : str1 = String::Flatten(str1);
252 0 : str2 = String::Flatten(str2);
253 :
254 : DisallowHeapAllocation no_gc;
255 0 : String::FlatContent flat1 = str1->GetFlatContent();
256 0 : String::FlatContent flat2 = str2->GetFlatContent();
257 :
258 0 : for (int i = 0; i < end; i++) {
259 0 : if (flat1.Get(i) != flat2.Get(i)) {
260 0 : return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
261 : }
262 : }
263 :
264 0 : return Smi::FromInt(str1_length - str2_length);
265 : }
266 :
267 : #ifndef V8_INTL_SUPPORT
268 : // ES6 section 21.1.3.12 String.prototype.normalize ( [form] )
269 : //
270 : // Simply checks the argument is valid and returns the string itself.
271 : // If internationalization is enabled, then intl.js will override this function
272 : // and provide the proper functionality, so this is just a fallback.
273 : BUILTIN(StringPrototypeNormalize) {
274 : HandleScope handle_scope(isolate);
275 : TO_THIS_STRING(string, "String.prototype.normalize");
276 :
277 : Handle<Object> form_input = args.atOrUndefined(isolate, 1);
278 : if (form_input->IsUndefined(isolate)) return *string;
279 :
280 : Handle<String> form;
281 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
282 : Object::ToString(isolate, form_input));
283 :
284 : if (!(String::Equals(form,
285 : isolate->factory()->NewStringFromStaticChars("NFC")) ||
286 : String::Equals(form,
287 : isolate->factory()->NewStringFromStaticChars("NFD")) ||
288 : String::Equals(form,
289 : isolate->factory()->NewStringFromStaticChars("NFKC")) ||
290 : String::Equals(form,
291 : isolate->factory()->NewStringFromStaticChars("NFKD")))) {
292 : Handle<String> valid_forms =
293 : isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
294 : THROW_NEW_ERROR_RETURN_FAILURE(
295 : isolate,
296 : NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
297 : }
298 :
299 : return *string;
300 : }
301 : #endif // !V8_INTL_SUPPORT
302 :
303 52302 : BUILTIN(StringPrototypeStartsWith) {
304 : HandleScope handle_scope(isolate);
305 35372 : TO_THIS_STRING(str, "String.prototype.startsWith");
306 :
307 : // Check if the search string is a regExp and fail if it is.
308 17154 : Handle<Object> search = args.atOrUndefined(isolate, 1);
309 17154 : Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
310 17154 : if (is_reg_exp.IsNothing()) {
311 : DCHECK(isolate->has_pending_exception());
312 0 : return isolate->heap()->exception();
313 : }
314 17154 : if (is_reg_exp.FromJust()) {
315 339 : THROW_NEW_ERROR_RETURN_FAILURE(
316 : isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
317 : isolate->factory()->NewStringFromStaticChars(
318 : "String.prototype.startsWith")));
319 : }
320 : Handle<String> search_string;
321 34082 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
322 : Object::ToString(isolate, search));
323 :
324 : Handle<Object> position = args.atOrUndefined(isolate, 2);
325 : int start;
326 :
327 17041 : if (position->IsUndefined(isolate)) {
328 : start = 0;
329 : } else {
330 2030 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
331 : Object::ToInteger(isolate, position));
332 2030 : start = str->ToValidIndex(*position);
333 : }
334 :
335 34082 : if (start + search_string->length() > str->length()) {
336 894 : return isolate->heap()->false_value();
337 : }
338 :
339 16147 : FlatStringReader str_reader(isolate, String::Flatten(str));
340 16147 : FlatStringReader search_reader(isolate, String::Flatten(search_string));
341 :
342 193480 : for (int i = 0; i < search_string->length(); i++) {
343 183950 : if (str_reader.Get(start + i) != search_reader.Get(i)) {
344 11382 : return isolate->heap()->false_value();
345 : }
346 : }
347 4765 : return isolate->heap()->true_value();
348 : }
349 :
350 : // ES6 section 21.1.3.27 String.prototype.trim ()
351 5510814 : BUILTIN(StringPrototypeTrim) {
352 : HandleScope scope(isolate);
353 3674416 : TO_THIS_STRING(string, "String.prototype.trim");
354 3673336 : return *String::Trim(string, String::kTrim);
355 : }
356 :
357 : // Non-standard WebKit extension
358 2646 : BUILTIN(StringPrototypeTrimLeft) {
359 : HandleScope scope(isolate);
360 1764 : TO_THIS_STRING(string, "String.prototype.trimLeft");
361 1764 : return *String::Trim(string, String::kTrimLeft);
362 : }
363 :
364 : // Non-standard WebKit extension
365 1512 : BUILTIN(StringPrototypeTrimRight) {
366 : HandleScope scope(isolate);
367 1008 : TO_THIS_STRING(string, "String.prototype.trimRight");
368 1008 : return *String::Trim(string, String::kTrimRight);
369 : }
370 :
371 : namespace {
372 :
373 : inline bool ToUpperOverflows(uc32 character) {
374 : // y with umlauts and the micro sign are the only characters that stop
375 : // fitting into one-byte when converting to uppercase.
376 : static const uc32 yuml_code = 0xff;
377 : static const uc32 micro_code = 0xb5;
378 0 : return (character == yuml_code || character == micro_code);
379 : }
380 :
381 : template <class Converter>
382 0 : MUST_USE_RESULT static Object* ConvertCaseHelper(
383 : Isolate* isolate, String* string, SeqString* result, int result_length,
384 : unibrow::Mapping<Converter, 128>* mapping) {
385 : DisallowHeapAllocation no_gc;
386 : // We try this twice, once with the assumption that the result is no longer
387 : // than the input and, if that assumption breaks, again with the exact
388 : // length. This may not be pretty, but it is nicer than what was here before
389 : // and I hereby claim my vaffel-is.
390 : //
391 : // NOTE: This assumes that the upper/lower case of an ASCII
392 : // character is also ASCII. This is currently the case, but it
393 : // might break in the future if we implement more context and locale
394 : // dependent upper/lower conversions.
395 : bool has_changed_character = false;
396 :
397 : // Convert all characters to upper case, assuming that they will fit
398 : // in the buffer
399 : StringCharacterStream stream(string);
400 : unibrow::uchar chars[Converter::kMaxWidth];
401 : // We can assume that the string is not empty
402 0 : uc32 current = stream.GetNext();
403 : bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
404 0 : for (int i = 0; i < result_length;) {
405 0 : bool has_next = stream.HasMore();
406 0 : uc32 next = has_next ? stream.GetNext() : 0;
407 0 : int char_length = mapping->get(current, next, chars);
408 0 : if (char_length == 0) {
409 : // The case conversion of this character is the character itself.
410 0 : result->Set(i, current);
411 0 : i++;
412 0 : } else if (char_length == 1 &&
413 : (ignore_overflow || !ToUpperOverflows(current))) {
414 : // Common case: converting the letter resulted in one character.
415 : DCHECK(static_cast<uc32>(chars[0]) != current);
416 0 : result->Set(i, chars[0]);
417 : has_changed_character = true;
418 0 : i++;
419 0 : } else if (result_length == string->length()) {
420 : bool overflows = ToUpperOverflows(current);
421 : // We've assumed that the result would be as long as the
422 : // input but here is a character that converts to several
423 : // characters. No matter, we calculate the exact length
424 : // of the result and try the whole thing again.
425 : //
426 : // Note that this leaves room for optimization. We could just
427 : // memcpy what we already have to the result string. Also,
428 : // the result string is the last object allocated we could
429 : // "realloc" it and probably, in the vast majority of cases,
430 : // extend the existing string to be able to hold the full
431 : // result.
432 : int next_length = 0;
433 0 : if (has_next) {
434 0 : next_length = mapping->get(next, 0, chars);
435 0 : if (next_length == 0) next_length = 1;
436 : }
437 0 : int current_length = i + char_length + next_length;
438 0 : while (stream.HasMore()) {
439 0 : current = stream.GetNext();
440 0 : overflows |= ToUpperOverflows(current);
441 : // NOTE: we use 0 as the next character here because, while
442 : // the next character may affect what a character converts to,
443 : // it does not in any case affect the length of what it convert
444 : // to.
445 0 : int char_length = mapping->get(current, 0, chars);
446 0 : if (char_length == 0) char_length = 1;
447 0 : current_length += char_length;
448 0 : if (current_length > String::kMaxLength) {
449 : AllowHeapAllocation allocate_error_and_return;
450 0 : THROW_NEW_ERROR_RETURN_FAILURE(isolate,
451 : NewInvalidStringLengthError());
452 : }
453 : }
454 : // Try again with the real length. Return signed if we need
455 : // to allocate a two-byte string for to uppercase.
456 0 : return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
457 0 : : Smi::FromInt(current_length);
458 : } else {
459 0 : for (int j = 0; j < char_length; j++) {
460 0 : result->Set(i, chars[j]);
461 0 : i++;
462 : }
463 : has_changed_character = true;
464 : }
465 : current = next;
466 : }
467 0 : if (has_changed_character) {
468 : return result;
469 : } else {
470 : // If we didn't actually change anything in doing the conversion
471 : // we simple return the result and let the converted string
472 : // become garbage; there is no reason to keep two identical strings
473 : // alive.
474 0 : return string;
475 : }
476 : }
477 :
478 : template <class Converter>
479 0 : MUST_USE_RESULT static Object* ConvertCase(
480 : Handle<String> s, Isolate* isolate,
481 : unibrow::Mapping<Converter, 128>* mapping) {
482 0 : s = String::Flatten(s);
483 : int length = s->length();
484 : // Assume that the string is not empty; we need this assumption later
485 0 : if (length == 0) return *s;
486 :
487 : // Simpler handling of ASCII strings.
488 : //
489 : // NOTE: This assumes that the upper/lower case of an ASCII
490 : // character is also ASCII. This is currently the case, but it
491 : // might break in the future if we implement more context and locale
492 : // dependent upper/lower conversions.
493 0 : if (s->IsOneByteRepresentationUnderneath()) {
494 : // Same length as input.
495 : Handle<SeqOneByteString> result =
496 0 : isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
497 : DisallowHeapAllocation no_gc;
498 0 : String::FlatContent flat_content = s->GetFlatContent();
499 : DCHECK(flat_content.IsFlat());
500 0 : bool has_changed_character = false;
501 : int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
502 0 : reinterpret_cast<char*>(result->GetChars()),
503 0 : reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
504 0 : length, &has_changed_character);
505 : // If not ASCII, we discard the result and take the 2 byte path.
506 0 : if (index_to_first_unprocessed == length)
507 0 : return has_changed_character ? *result : *s;
508 : }
509 :
510 : Handle<SeqString> result; // Same length as input.
511 0 : if (s->IsOneByteRepresentation()) {
512 0 : result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
513 : } else {
514 0 : result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
515 : }
516 :
517 0 : Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
518 0 : if (answer->IsException(isolate) || answer->IsString()) return answer;
519 :
520 : DCHECK(answer->IsSmi());
521 : length = Smi::cast(answer)->value();
522 0 : if (s->IsOneByteRepresentation() && length > 0) {
523 0 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
524 : isolate, result, isolate->factory()->NewRawOneByteString(length));
525 : } else {
526 0 : if (length < 0) length = -length;
527 0 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
528 : isolate, result, isolate->factory()->NewRawTwoByteString(length));
529 : }
530 0 : return ConvertCaseHelper(isolate, *s, *result, length, mapping);
531 : }
532 :
533 : } // namespace
534 :
535 0 : BUILTIN(StringPrototypeToLocaleLowerCase) {
536 : HandleScope scope(isolate);
537 0 : TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
538 : return ConvertCase(string, isolate,
539 0 : isolate->runtime_state()->to_lower_mapping());
540 : }
541 :
542 0 : BUILTIN(StringPrototypeToLocaleUpperCase) {
543 : HandleScope scope(isolate);
544 0 : TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
545 : return ConvertCase(string, isolate,
546 0 : isolate->runtime_state()->to_upper_mapping());
547 : }
548 :
549 0 : BUILTIN(StringPrototypeToLowerCase) {
550 : HandleScope scope(isolate);
551 0 : TO_THIS_STRING(string, "String.prototype.toLowerCase");
552 : return ConvertCase(string, isolate,
553 0 : isolate->runtime_state()->to_lower_mapping());
554 : }
555 :
556 0 : BUILTIN(StringPrototypeToUpperCase) {
557 : HandleScope scope(isolate);
558 0 : TO_THIS_STRING(string, "String.prototype.toUpperCase");
559 : return ConvertCase(string, isolate,
560 0 : isolate->runtime_state()->to_upper_mapping());
561 : }
562 :
563 : } // namespace internal
564 : } // namespace v8
|