v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
runtime-strings.cc
Go to the documentation of this file.
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
6#include "src/heap/heap-inl.h"
10#include "src/objects/slots.h"
11#include "src/objects/smi.h"
15
16namespace v8 {
17namespace internal {
18
19RUNTIME_FUNCTION(Runtime_GetSubstitution) {
20 HandleScope scope(isolate);
21 DCHECK_EQ(5, args.length());
22 DirectHandle<String> matched = args.at<String>(0);
23 DirectHandle<String> subject = args.at<String>(1);
24 int position = args.smi_value_at(2);
25 DirectHandle<String> replacement = args.at<String>(3);
26 int start_index = args.smi_value_at(4);
27
28 // A simple match without captures.
29 class SimpleMatch : public String::Match {
30 public:
31 SimpleMatch(DirectHandle<String> match, DirectHandle<String> prefix,
33 : match_(match), prefix_(prefix), suffix_(suffix) {}
34
35 DirectHandle<String> GetMatch() override { return match_; }
36 DirectHandle<String> GetPrefix() override { return prefix_; }
37 DirectHandle<String> GetSuffix() override { return suffix_; }
38
39 int CaptureCount() override { return 0; }
40 bool HasNamedCaptures() override { return false; }
41 MaybeDirectHandle<String> GetCapture(int i, bool* capture_exists) override {
42 *capture_exists = false;
43 return match_; // Return arbitrary string handle.
44 }
46 CaptureState* state) override {
48 }
49
50 private:
51 DirectHandle<String> match_, prefix_, suffix_;
52 };
53
55 isolate->factory()->NewSubString(subject, 0, position);
56 DirectHandle<String> suffix = isolate->factory()->NewSubString(
57 subject, position + matched->length(), subject->length());
58 SimpleMatch match(matched, prefix, suffix);
59
61 isolate,
62 String::GetSubstitution(isolate, &match, replacement, start_index));
63}
64
65// This may return an empty MaybeDirectHandle if an exception is thrown or
66// we abort due to reaching the recursion limit.
68 Isolate* isolate, DirectHandle<String> subject, DirectHandle<String> search,
69 DirectHandle<String> replace, bool* found, int recursion_limit) {
70 StackLimitCheck stackLimitCheck(isolate);
71 if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
73 }
74 recursion_limit--;
75 if (IsConsString(*subject)) {
76 Tagged<ConsString> cons = Cast<ConsString>(*subject);
77 DirectHandle<String> first(cons->first(), isolate);
78 DirectHandle<String> second(cons->second(), isolate);
79 DirectHandle<String> new_first;
80 if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
81 recursion_limit).ToHandle(&new_first)) {
83 }
84 if (*found) return isolate->factory()->NewConsString(new_first, second);
85
86 DirectHandle<String> new_second;
87 if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
88 recursion_limit)
89 .ToHandle(&new_second)) {
91 }
92 if (*found) return isolate->factory()->NewConsString(first, new_second);
93
94 return subject;
95 } else {
96 int index = String::IndexOf(isolate, subject, search, 0);
97 if (index == -1) return subject;
98 *found = true;
100 isolate->factory()->NewSubString(subject, 0, index);
103 isolate, cons1, isolate->factory()->NewConsString(first, replace));
105 isolate->factory()->NewSubString(subject, index + 1, subject->length());
106 return isolate->factory()->NewConsString(cons1, second);
107 }
108}
109
110RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
111 HandleScope scope(isolate);
112 DCHECK_EQ(3, args.length());
113 DirectHandle<String> subject = args.at<String>(0);
114 DirectHandle<String> search = args.at<String>(1);
115 DirectHandle<String> replace = args.at<String>(2);
116
117 // If the cons string tree is too deep, we simply abort the recursion and
118 // retry with a flattened subject string.
119 const int kRecursionLimit = 0x1000;
120 bool found = false;
122 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
123 kRecursionLimit).ToHandle(&result)) {
124 return *result;
125 }
126 if (isolate->has_exception()) return ReadOnlyRoots(isolate).exception();
127
128 subject = String::Flatten(isolate, subject);
129 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
130 kRecursionLimit).ToHandle(&result)) {
131 return *result;
132 }
133 if (isolate->has_exception()) return ReadOnlyRoots(isolate).exception();
134 // In case of empty handle and no exception we have stack overflow.
135 return isolate->StackOverflow();
136}
137
138RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
139 HandleScope handle_scope(isolate);
140 return String::LastIndexOf(isolate, args.at(0), args.at(1),
141 isolate->factory()->undefined_value());
142}
143
144RUNTIME_FUNCTION(Runtime_StringSubstring) {
145 HandleScope scope(isolate);
146 DCHECK_EQ(3, args.length());
147 DirectHandle<String> string = args.at<String>(0);
148 int start = args.smi_value_at(1);
149 int end = args.smi_value_at(2);
150 DCHECK_LE(0, start);
152 DCHECK_LE(end, string->length());
153 return *isolate->factory()->NewSubString(string, start, end);
154}
155
156RUNTIME_FUNCTION(Runtime_StringAdd) {
157 // This is used by Wasm.
158 SaveAndClearThreadInWasmFlag non_wasm_scope(isolate);
159 HandleScope scope(isolate);
160 DCHECK_EQ(2, args.length());
161 DirectHandle<String> str1 = args.at<String>(0);
162 DirectHandle<String> str2 = args.at<String>(1);
164 isolate->factory()->NewConsString(str1, str2));
165}
166
167
168RUNTIME_FUNCTION(Runtime_InternalizeString) {
169 HandleScope handles(isolate);
170 DCHECK_EQ(1, args.length());
171 DirectHandle<String> string = args.at<String>(0);
172 return *isolate->factory()->InternalizeString(string);
173}
174
175RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
176 SaveAndClearThreadInWasmFlag non_wasm_scope(isolate);
177 HandleScope handle_scope(isolate);
178 DCHECK_EQ(2, args.length());
179
180 DirectHandle<String> subject = args.at<String>(0);
181 uint32_t i = NumberToUint32(args[1]);
182
183 // Flatten the string. If someone wants to get a char at an index
184 // in a cons string, it is likely that more indices will be
185 // accessed.
186 subject = String::Flatten(isolate, subject);
187
188 if (i >= static_cast<uint32_t>(subject->length())) {
189 return ReadOnlyRoots(isolate).nan_value();
190 }
191
192 return Smi::FromInt(subject->Get(i));
193}
194
195RUNTIME_FUNCTION(Runtime_StringCodePointAt) {
196 HandleScope handle_scope(isolate);
197 DCHECK_EQ(2, args.length());
198
199 DirectHandle<String> subject = args.at<String>(0);
200 uint32_t i = NumberToUint32(args[1]);
201
202 // Flatten the string. If someone wants to get a char at an index
203 // in a cons string, it is likely that more indices will be
204 // accessed.
205 subject = String::Flatten(isolate, subject);
206
207 if (i >= static_cast<uint32_t>(subject->length())) {
208 return ReadOnlyRoots(isolate).nan_value();
209 }
210
211 int first_code_point = subject->Get(i);
212 if ((first_code_point & 0xFC00) != 0xD800) {
213 return Smi::FromInt(first_code_point);
214 }
215
216 if (i + 1 >= static_cast<uint32_t>(subject->length())) {
217 return Smi::FromInt(first_code_point);
218 }
219
220 int second_code_point = subject->Get(i + 1);
221 if ((second_code_point & 0xFC00) != 0xDC00) {
222 return Smi::FromInt(first_code_point);
223 }
224
225 int surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00;
226 return Smi::FromInt((first_code_point << 10) +
227 (second_code_point + surrogate_offset));
228}
229
230RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
231 HandleScope scope(isolate);
232 DCHECK_EQ(3, args.length());
234
235 int array_length = args.smi_value_at(1);
236
237 DirectHandle<String> special = args.at<String>(2);
238
239 // This assumption is used by the slice encoding in one or two smis.
241
242 int special_length = special->length();
243
244 int length;
245 bool one_byte = special->IsOneByteRepresentation();
246
247 {
249 Tagged<FixedArray> fixed_array = *array;
250
251 if (array_length == 0) {
252 return ReadOnlyRoots(isolate).empty_string();
253 } else if (array_length == 1) {
254 Tagged<Object> first = fixed_array->get(0);
255 if (IsString(first)) return first;
256 }
257 length = StringBuilderConcatLength(special_length, fixed_array,
258 array_length, &one_byte);
259 }
260
261 if (length == -1) {
262 return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
263 }
264 if (length == 0) {
265 return ReadOnlyRoots(isolate).empty_string();
266 }
267
268 if (one_byte) {
271 isolate, answer, isolate->factory()->NewRawOneByteString(length));
273 StringBuilderConcatHelper(*special, answer->GetChars(no_gc), *array,
274 array_length);
275 return *answer;
276 } else {
279 isolate, answer, isolate->factory()->NewRawTwoByteString(length));
281 StringBuilderConcatHelper(*special, answer->GetChars(no_gc), *array,
282 array_length);
283 return *answer;
284 }
285}
286
287// Converts a String to JSArray.
288// For example, "foo" => ["f", "o", "o"].
289RUNTIME_FUNCTION(Runtime_StringToArray) {
290 HandleScope scope(isolate);
291 DCHECK_EQ(2, args.length());
293 uint32_t limit = NumberToUint32(args[1]);
294
295 s = String::Flatten(isolate, s);
296 const int length =
297 static_cast<int>(std::min(static_cast<uint32_t>(s->length()), limit));
298
299 DirectHandle<FixedArray> elements = isolate->factory()->NewFixedArray(length);
300 bool elements_are_initialized = false;
301
302 if (s->IsFlat() && s->IsOneByteRepresentation()) {
304 String::FlatContent content = s->GetFlatContent(no_gc);
305 // Use pre-initialized single characters to initialize all the elements.
306 // This can be false if the string is sliced from an externalized
307 // two-byte string that has only one-byte chars, in that case we will do
308 // a LookupSingleCharacterStringFromCode for each of the characters.
309 if (content.IsOneByte()) {
311 Tagged<FixedArray> one_byte_table =
312 isolate->heap()->single_character_string_table();
313 for (int i = 0; i < length; ++i) {
314 Tagged<Object> value = one_byte_table->get(chars[i]);
315 DCHECK(IsString(value));
317 // The single-character strings are in RO space so it should
318 // be safe to skip the write barriers.
319 elements->set(i, value, SKIP_WRITE_BARRIER);
320 }
321 elements_are_initialized = true;
322 }
323 }
324
325 if (!elements_are_initialized) {
326 for (int i = 0; i < length; ++i) {
328 isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
329 elements->set(i, *str);
330 }
331 }
332
333#ifdef DEBUG
334 for (int i = 0; i < length; ++i) {
335 DCHECK_EQ(Cast<String>(elements->get(i))->length(), 1);
336 }
337#endif
338
339 return *isolate->factory()->NewJSArrayWithElements(elements);
340}
341
342RUNTIME_FUNCTION(Runtime_StringLessThan) {
343 HandleScope handle_scope(isolate);
344 DCHECK_EQ(2, args.length());
349 return isolate->heap()->ToBoolean(
350 ComparisonResultToBool(Operation::kLessThan, result));
351}
352
353RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
354 HandleScope handle_scope(isolate);
355 DCHECK_EQ(2, args.length());
360 return isolate->heap()->ToBoolean(
361 ComparisonResultToBool(Operation::kLessThanOrEqual, result));
362}
363
364RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
365 HandleScope handle_scope(isolate);
366 DCHECK_EQ(2, args.length());
371 return isolate->heap()->ToBoolean(
372 ComparisonResultToBool(Operation::kGreaterThan, result));
373}
374
375RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
376 HandleScope handle_scope(isolate);
377 DCHECK_EQ(2, args.length());
382 return isolate->heap()->ToBoolean(
383 ComparisonResultToBool(Operation::kGreaterThanOrEqual, result));
384}
385
386RUNTIME_FUNCTION(Runtime_StringEqual) {
387 SaveAndClearThreadInWasmFlag non_wasm_scope(isolate);
388 HandleScope handle_scope(isolate);
389 DCHECK_EQ(2, args.length());
390#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
393#else
394 // This function can be called from Wasm: optimized Wasm code calls
395 // straight to the "StringEqual" builtin, which tail-calls here. So on
396 // the stack, the CEntryStub's EXIT frame will sit right on top of the
397 // Wasm frame; and Wasm frames don't scan their outgoing parameters (in
398 // order to support tail-calls between Wasm functions), while the EXIT
399 // frame doesn't scan its incoming parameters (because it expects to be
400 // called from JS).
401 // Working around this by calling through a trampoline builtin is slow.
402 // Teaching the stack walker to be smarter has proven to be difficult.
403 // In the future, Conservative Stack Scanning will trivially solve the
404 // problem. In the meantime, we can work around it by explicitly creating
405 // handles here (rather than treating the on-stack arguments as handles).
406 DirectHandle<String> x(*args.at<String>(0), isolate);
407 DirectHandle<String> y(*args.at<String>(1), isolate);
408#endif // V8_ENABLE_CONSERVATIVE_STACK_SCANNING
409 return isolate->heap()->ToBoolean(String::Equals(isolate, x, y));
410}
411
412RUNTIME_FUNCTION(Runtime_StringCompare) {
413 SaveAndClearThreadInWasmFlag non_wasm_scope(isolate);
414 DCHECK_EQ(2, args.length());
415 HandleScope scope(isolate);
416 DirectHandle<String> lhs(Cast<String>(args[0]), isolate);
417 DirectHandle<String> rhs(Cast<String>(args[1]), isolate);
418 ComparisonResult result = String::Compare(isolate, lhs, rhs);
420 return Smi::FromInt(static_cast<int>(result));
421}
422
423RUNTIME_FUNCTION(Runtime_FlattenString) {
424 HandleScope scope(isolate);
425 DCHECK_EQ(1, args.length());
426 DirectHandle<String> str = args.at<String>(0);
427 return *String::Flatten(isolate, str);
428}
429
430RUNTIME_FUNCTION(Runtime_StringMaxLength) {
431 SealHandleScope shs(isolate);
433}
434
435RUNTIME_FUNCTION(Runtime_StringEscapeQuotes) {
436 HandleScope handle_scope(isolate);
437 DCHECK_EQ(1, args.length());
438 DirectHandle<String> string = args.at<String>(0);
439
440 // Equivalent to global replacement `string.replace(/"/g, "&quot")`, but this
441 // does not modify any global state (e.g. the regexp match info).
442
443 const int string_length = string->length();
444 DirectHandle<String> quotes =
445 isolate->factory()->LookupSingleCharacterStringFromCode('"');
446
447 int quote_index = String::IndexOf(isolate, string, quotes, 0);
448
449 // No quotes, nothing to do.
450 if (quote_index == -1) return *string;
451
452 // Find all quotes.
453 std::vector<int> indices = {quote_index};
454 while (quote_index + 1 < string_length) {
455 quote_index = String::IndexOf(isolate, string, quotes, quote_index + 1);
456 if (quote_index == -1) break;
457 indices.emplace_back(quote_index);
458 }
459
460 // Build the replacement string.
461 DirectHandle<String> replacement =
462 isolate->factory()->NewStringFromAsciiChecked("&quot;");
463 const int estimated_part_count = static_cast<int>(indices.size()) * 2 + 1;
464 ReplacementStringBuilder builder(isolate->heap(), string,
465 estimated_part_count);
466
467 int prev_index = -1; // Start at -1 to avoid special-casing the first match.
468 for (int index : indices) {
469 const int slice_start = prev_index + 1;
470 const int slice_end = index;
471 if (slice_end > slice_start) {
472 builder.AddSubjectSlice(slice_start, slice_end);
473 }
474 builder.AddString(replacement);
475 prev_index = index;
476 }
477
478 if (prev_index < string_length - 1) {
479 builder.AddSubjectSlice(prev_index + 1, string_length);
480 }
481
484 return *result;
485}
486
487RUNTIME_FUNCTION(Runtime_StringIsWellFormed) {
488 HandleScope handle_scope(isolate);
489 DCHECK_EQ(1, args.length());
490 DirectHandle<String> string = args.at<String>(0);
491 return isolate->heap()->ToBoolean(
492 String::IsWellFormedUnicode(isolate, string));
493}
494
495RUNTIME_FUNCTION(Runtime_StringToWellFormed) {
496 HandleScope handle_scope(isolate);
497 DCHECK_EQ(1, args.length());
498 DirectHandle<String> source = args.at<String>(0);
499 if (String::IsWellFormedUnicode(isolate, source)) return *source;
500 // String::IsWellFormedUnicode would have returned true above otherwise.
502 const int length = source->length();
504 isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
506 String::FlatContent source_contents = source->GetFlatContent(no_gc);
507 DCHECK(source_contents.IsFlat());
508 const uint16_t* source_data = source_contents.ToUC16Vector().begin();
509 uint16_t* dest_data = dest->GetChars(no_gc);
510 unibrow::Utf16::ReplaceUnpairedSurrogates(source_data, dest_data, length);
511 return *dest;
512}
513
514} // namespace internal
515} // namespace v8
static void ReplaceUnpairedSurrogates(const uint16_t *source_code_units, uint16_t *dest_code_units, size_t length)
Definition unicode.cc:244
constexpr T * begin() const
Definition vector.h:96
static V8_EXPORT_PRIVATE bool Contains(Address address)
static void AddSubjectSlice(FixedArrayBuilder *builder, int from, int to)
MaybeDirectHandle< String > ToString()
void AddString(DirectHandle< String > string)
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr int kMaxValue
Definition smi.h:101
base::Vector< const uint8_t > ToOneByteVector() const
Definition string.h:139
base::Vector< const base::uc16 > ToUC16Vector() const
Definition string.h:145
static const uint32_t kMaxLength
Definition string.h:511
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
static Tagged< Object > IndexOf(Isolate *isolate, DirectHandle< Object > receiver, DirectHandle< Object > search, DirectHandle< Object > position)
Definition string.cc:1426
static V8_WARN_UNUSED_RESULT ComparisonResult Compare(Isolate *isolate, DirectHandle< String > x, DirectHandle< String > y)
Definition string.cc:1353
uint32_t length() const
Definition string-inl.h:127
static bool IsOneByteRepresentationUnderneath(Tagged< String > string)
Definition string-inl.h:373
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > GetSubstitution(Isolate *isolate, Match *match, DirectHandle< String > replacement, uint32_t start_index=0)
Definition string.cc:1495
static bool IsWellFormedUnicode(Isolate *isolate, DirectHandle< String > string)
bool Equals(Tagged< String > other) const
Definition string-inl.h:535
static Tagged< Object > LastIndexOf(Isolate *isolate, DirectHandle< Object > receiver, DirectHandle< Object > search, DirectHandle< Object > position)
Definition string.cc:1689
int start
int end
#define RUNTIME_FUNCTION(Name)
Definition arguments.h:162
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define RETURN_RESULT_OR_FAILURE(isolate, call)
Definition isolate.h:264
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
double second
ZoneVector< RpoNumber > & result
int x
int position
Definition liveedit.cc:290
InstructionOperand source
@ SKIP_WRITE_BARRIER
Definition objects.h:52
bool ComparisonResultToBool(Operation op, ComparisonResult result)
Definition objects.cc:164
MaybeDirectHandle< String > StringReplaceOneCharWithString(Isolate *isolate, DirectHandle< String > subject, DirectHandle< String > search, DirectHandle< String > replace, bool *found, int recursion_limit)
void StringBuilderConcatHelper(Tagged< String > special, sinkchar *sink, Tagged< FixedArray > fixed_array, int array_length)
int StringBuilderConcatLength(int special_length, Tagged< FixedArray > fixed_array, int array_length, bool *one_byte)
uint32_t NumberToUint32(Tagged< Object > number)
template const char * string
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
DirectHandle< String > match_
#define UNREACHABLE()
Definition logging.h:67
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485