runtime-strings.cc 14.9 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/execution/arguments-inl.h"
6
#include "src/heap/heap-inl.h"
7
#include "src/numbers/conversions.h"
8
#include "src/objects/js-array-inl.h"
9
#include "src/objects/objects-inl.h"
10
#include "src/objects/slots.h"
11
#include "src/objects/smi.h"
12
#include "src/strings/string-builder-inl.h"
13 14 15 16

namespace v8 {
namespace internal {

17 18
RUNTIME_FUNCTION(Runtime_GetSubstitution) {
  HandleScope scope(isolate);
19
  DCHECK_EQ(5, args.length());
20 21 22 23 24
  Handle<String> matched = args.at<String>(0);
  Handle<String> subject = args.at<String>(1);
  int position = args.smi_value_at(2);
  Handle<String> replacement = args.at<String>(3);
  int start_index = args.smi_value_at(4);
25 26 27 28 29 30 31 32 33

  // A simple match without captures.
  class SimpleMatch : public String::Match {
   public:
    SimpleMatch(Handle<String> match, Handle<String> prefix,
                Handle<String> suffix)
        : match_(match), prefix_(prefix), suffix_(suffix) {}

    Handle<String> GetMatch() override { return match_; }
34 35 36 37 38
    Handle<String> GetPrefix() override { return prefix_; }
    Handle<String> GetSuffix() override { return suffix_; }

    int CaptureCount() override { return 0; }
    bool HasNamedCaptures() override { return false; }
39 40 41 42
    MaybeHandle<String> GetCapture(int i, bool* capture_exists) override {
      *capture_exists = false;
      return match_;  // Return arbitrary string handle.
    }
43
    MaybeHandle<String> GetNamedCapture(Handle<String> name,
44
                                        CaptureState* state) override {
45 46
      UNREACHABLE();
    }
47 48 49 50 51 52 53 54 55 56 57 58

   private:
    Handle<String> match_, prefix_, suffix_;
  };

  Handle<String> prefix =
      isolate->factory()->NewSubString(subject, 0, position);
  Handle<String> suffix = isolate->factory()->NewSubString(
      subject, position + matched->length(), subject->length());
  SimpleMatch match(matched, prefix, suffix);

  RETURN_RESULT_OR_FAILURE(
59 60
      isolate,
      String::GetSubstitution(isolate, &match, replacement, start_index));
61 62
}

63 64 65 66 67 68 69 70 71 72 73
// This may return an empty MaybeHandle if an exception is thrown or
// we abort due to reaching the recursion limit.
MaybeHandle<String> StringReplaceOneCharWithString(
    Isolate* isolate, Handle<String> subject, Handle<String> search,
    Handle<String> replace, bool* found, int recursion_limit) {
  StackLimitCheck stackLimitCheck(isolate);
  if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
    return MaybeHandle<String>();
  }
  recursion_limit--;
  if (subject->IsConsString()) {
74
    ConsString cons = ConsString::cast(*subject);
75 76
    Handle<String> first = handle(cons.first(), isolate);
    Handle<String> second = handle(cons.second(), isolate);
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
    Handle<String> new_first;
    if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
                                        recursion_limit).ToHandle(&new_first)) {
      return MaybeHandle<String>();
    }
    if (*found) return isolate->factory()->NewConsString(new_first, second);

    Handle<String> new_second;
    if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
                                        recursion_limit)
             .ToHandle(&new_second)) {
      return MaybeHandle<String>();
    }
    if (*found) return isolate->factory()->NewConsString(first, new_second);

    return subject;
  } else {
94
    int index = String::IndexOf(isolate, subject, search, 0);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    if (index == -1) return subject;
    *found = true;
    Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
    Handle<String> cons1;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, cons1, isolate->factory()->NewConsString(first, replace),
        String);
    Handle<String> second =
        isolate->factory()->NewSubString(subject, index + 1, subject->length());
    return isolate->factory()->NewConsString(cons1, second);
  }
}

RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
  HandleScope scope(isolate);
110
  DCHECK_EQ(3, args.length());
111 112 113
  Handle<String> subject = args.at<String>(0);
  Handle<String> search = args.at<String>(1);
  Handle<String> replace = args.at<String>(2);
114 115 116 117 118 119 120 121 122 123

  // If the cons string tree is too deep, we simply abort the recursion and
  // retry with a flattened subject string.
  const int kRecursionLimit = 0x1000;
  bool found = false;
  Handle<String> result;
  if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
                                     kRecursionLimit).ToHandle(&result)) {
    return *result;
  }
124 125
  if (isolate->has_pending_exception())
    return ReadOnlyRoots(isolate).exception();
126

127
  subject = String::Flatten(isolate, subject);
128 129 130 131
  if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
                                     kRecursionLimit).ToHandle(&result)) {
    return *result;
  }
132 133
  if (isolate->has_pending_exception())
    return ReadOnlyRoots(isolate).exception();
134 135
  // In case of empty handle and no pending exception we have stack overflow.
  return isolate->StackOverflow();
136 137 138
}

RUNTIME_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
}

144
RUNTIME_FUNCTION(Runtime_StringSubstring) {
145
  HandleScope scope(isolate);
146
  DCHECK_EQ(3, args.length());
147 148 149
  Handle<String> string = args.at<String>(0);
  int start = args.smi_value_at(1);
  int end = args.smi_value_at(2);
150 151 152
  DCHECK_LE(0, start);
  DCHECK_LE(start, end);
  DCHECK_LE(end, string->length());
153 154 155
  return *isolate->factory()->NewSubString(string, start, end);
}

156
RUNTIME_FUNCTION(Runtime_StringAdd) {
157
  HandleScope scope(isolate);
158
  DCHECK_EQ(2, args.length());
159 160
  Handle<String> str1 = args.at<String>(0);
  Handle<String> str2 = args.at<String>(1);
161 162
  RETURN_RESULT_OR_FAILURE(isolate,
                           isolate->factory()->NewConsString(str1, str2));
163 164 165 166 167
}


RUNTIME_FUNCTION(Runtime_InternalizeString) {
  HandleScope handles(isolate);
168
  DCHECK_EQ(1, args.length());
169
  Handle<String> string = args.at<String>(0);
170 171 172
  return *isolate->factory()->InternalizeString(string);
}

173
RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
174
  HandleScope handle_scope(isolate);
175
  DCHECK_EQ(2, args.length());
176

177 178
  Handle<String> subject = args.at<String>(0);
  uint32_t i = NumberToUint32(args[1]);
179 180 181 182

  // Flatten the string.  If someone wants to get a char at an index
  // in a cons string, it is likely that more indices will be
  // accessed.
183
  subject = String::Flatten(isolate, subject);
184 185

  if (i >= static_cast<uint32_t>(subject->length())) {
186
    return ReadOnlyRoots(isolate).nan_value();
187 188 189 190 191 192 193
  }

  return Smi::FromInt(subject->Get(i));
}

RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
  HandleScope scope(isolate);
194
  DCHECK_EQ(3, args.length());
195
  Handle<JSArray> array = args.at<JSArray>(0);
196
  int32_t array_length;
197
  if (!args[1].ToInt32(&array_length)) {
198 199
    THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
  }
200
  Handle<String> special = args.at<String>(2);
201 202

  size_t actual_array_length = 0;
203
  CHECK(TryNumberToSize(array->length(), &actual_array_length));
204
  CHECK_GE(array_length, 0);
205
  CHECK(static_cast<size_t>(array_length) <= actual_array_length);
206 207

  // This assumption is used by the slice encoding in one or two smis.
208
  DCHECK_GE(Smi::kMaxValue, String::kMaxLength);
209

210
  CHECK(array->HasFastElements());
211 212 213
  JSObject::EnsureCanContainHeapObjectElements(array);

  int special_length = special->length();
214
  if (!array->HasObjectElements()) {
215
    return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
216 217 218
  }

  int length;
219
  bool one_byte = special->IsOneByteRepresentation();
220 221

  {
222
    DisallowGarbageCollection no_gc;
223
    FixedArray fixed_array = FixedArray::cast(array->elements());
224 225
    if (fixed_array.length() < array_length) {
      array_length = fixed_array.length();
226 227 228
    }

    if (array_length == 0) {
229
      return ReadOnlyRoots(isolate).empty_string();
230
    } else if (array_length == 1) {
231 232
      Object first = fixed_array.get(0);
      if (first.IsString()) return first;
233 234 235 236 237 238
    }
    length = StringBuilderConcatLength(special_length, fixed_array,
                                       array_length, &one_byte);
  }

  if (length == -1) {
239
    return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
240
  }
241
  if (length == 0) {
242
    return ReadOnlyRoots(isolate).empty_string();
243
  }
244 245 246 247 248

  if (one_byte) {
    Handle<SeqOneByteString> answer;
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
        isolate, answer, isolate->factory()->NewRawOneByteString(length));
249
    DisallowGarbageCollection no_gc;
250
    StringBuilderConcatHelper(*special, answer->GetChars(no_gc),
251 252 253 254 255 256 257
                              FixedArray::cast(array->elements()),
                              array_length);
    return *answer;
  } else {
    Handle<SeqTwoByteString> answer;
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
        isolate, answer, isolate->factory()->NewRawTwoByteString(length));
258
    DisallowGarbageCollection no_gc;
259
    StringBuilderConcatHelper(*special, answer->GetChars(no_gc),
260 261 262 263 264 265 266 267 268 269
                              FixedArray::cast(array->elements()),
                              array_length);
    return *answer;
  }
}

// Converts a String to JSArray.
// For example, "foo" => ["f", "o", "o"].
RUNTIME_FUNCTION(Runtime_StringToArray) {
  HandleScope scope(isolate);
270
  DCHECK_EQ(2, args.length());
271 272
  Handle<String> s = args.at<String>(0);
  uint32_t limit = NumberToUint32(args[1]);
273

274
  s = String::Flatten(isolate, s);
275 276
  const int length =
      static_cast<int>(std::min(static_cast<uint32_t>(s->length()), limit));
277

278
  Handle<FixedArray> elements = isolate->factory()->NewFixedArray(length);
279 280
  bool elements_are_initialized = false;

281
  if (s->IsFlat() && s->IsOneByteRepresentation()) {
282
    DisallowGarbageCollection no_gc;
283
    String::FlatContent content = s->GetFlatContent(no_gc);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    // Use pre-initialized single characters to intialize all the elements.
    // This can be false if the string is sliced from an externalized
    // two-byte string that has only one-byte chars, in that case we will do
    // a LookupSingleCharacterStringFromCode for each of the characters.
    if (content.IsOneByte()) {
      base::Vector<const uint8_t> chars = content.ToOneByteVector();
      FixedArray one_byte_table =
          isolate->heap()->single_character_string_table();
      for (int i = 0; i < length; ++i) {
        Object value = one_byte_table.get(chars[i]);
        DCHECK(value.IsString());
        DCHECK(ReadOnlyHeap::Contains(HeapObject::cast(value)));
        // The single-character strings are in RO space so it should
        // be safe to skip the write barriers.
        elements->set(i, value, SKIP_WRITE_BARRIER);
      }
      elements_are_initialized = true;
301
    }
302 303 304
  }

  if (!elements_are_initialized) {
305 306 307 308 309
    for (int i = 0; i < length; ++i) {
      Handle<Object> str =
          isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
      elements->set(i, *str);
    }
310 311 312 313
  }

#ifdef DEBUG
  for (int i = 0; i < length; ++i) {
314
    DCHECK_EQ(String::cast(elements->get(i)).length(), 1);
315 316 317 318 319 320
  }
#endif

  return *isolate->factory()->NewJSArrayWithElements(elements);
}

321 322 323
RUNTIME_FUNCTION(Runtime_StringLessThan) {
  HandleScope handle_scope(isolate);
  DCHECK_EQ(2, args.length());
324 325
  Handle<String> x = args.at<String>(0);
  Handle<String> y = args.at<String>(1);
326
  ComparisonResult result = String::Compare(isolate, x, y);
327 328 329
  DCHECK_NE(result, ComparisonResult::kUndefined);
  return isolate->heap()->ToBoolean(
      ComparisonResultToBool(Operation::kLessThan, result));
330 331 332 333 334
}

RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
  HandleScope handle_scope(isolate);
  DCHECK_EQ(2, args.length());
335 336
  Handle<String> x = args.at<String>(0);
  Handle<String> y = args.at<String>(1);
337
  ComparisonResult result = String::Compare(isolate, x, y);
338 339 340
  DCHECK_NE(result, ComparisonResult::kUndefined);
  return isolate->heap()->ToBoolean(
      ComparisonResultToBool(Operation::kLessThanOrEqual, result));
341 342 343 344 345
}

RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
  HandleScope handle_scope(isolate);
  DCHECK_EQ(2, args.length());
346 347
  Handle<String> x = args.at<String>(0);
  Handle<String> y = args.at<String>(1);
348
  ComparisonResult result = String::Compare(isolate, x, y);
349 350 351
  DCHECK_NE(result, ComparisonResult::kUndefined);
  return isolate->heap()->ToBoolean(
      ComparisonResultToBool(Operation::kGreaterThan, result));
352 353 354 355 356
}

RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
  HandleScope handle_scope(isolate);
  DCHECK_EQ(2, args.length());
357 358
  Handle<String> x = args.at<String>(0);
  Handle<String> y = args.at<String>(1);
359
  ComparisonResult result = String::Compare(isolate, x, y);
360 361 362
  DCHECK_NE(result, ComparisonResult::kUndefined);
  return isolate->heap()->ToBoolean(
      ComparisonResultToBool(Operation::kGreaterThanOrEqual, result));
363 364
}

365
RUNTIME_FUNCTION(Runtime_StringEqual) {
366
  HandleScope handle_scope(isolate);
367
  DCHECK_EQ(2, args.length());
368 369
  Handle<String> x = args.at<String>(0);
  Handle<String> y = args.at<String>(1);
370
  return isolate->heap()->ToBoolean(String::Equals(isolate, x, y));
371 372
}

373 374
RUNTIME_FUNCTION(Runtime_FlattenString) {
  HandleScope scope(isolate);
375
  DCHECK_EQ(1, args.length());
376
  Handle<String> str = args.at<String>(0);
377
  return *String::Flatten(isolate, str);
378 379
}

380 381 382 383 384
RUNTIME_FUNCTION(Runtime_StringMaxLength) {
  SealHandleScope shs(isolate);
  return Smi::FromInt(String::kMaxLength);
}

385 386 387
RUNTIME_FUNCTION(Runtime_StringEscapeQuotes) {
  HandleScope handle_scope(isolate);
  DCHECK_EQ(1, args.length());
388
  Handle<String> string = args.at<String>(0);
389 390 391 392 393 394 395 396

  // Equivalent to global replacement `string.replace(/"/g, "&quot")`, but this
  // does not modify any global state (e.g. the regexp match info).

  const int string_length = string->length();
  Handle<String> quotes =
      isolate->factory()->LookupSingleCharacterStringFromCode('"');

397
  int quote_index = String::IndexOf(isolate, string, quotes, 0);
398 399

  // No quotes, nothing to do.
400
  if (quote_index == -1) return *string;
401 402

  // Find all quotes.
403 404 405 406 407
  std::vector<int> indices = {quote_index};
  while (quote_index + 1 < string_length) {
    quote_index = String::IndexOf(isolate, string, quotes, quote_index + 1);
    if (quote_index == -1) break;
    indices.emplace_back(quote_index);
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
  }

  // Build the replacement string.
  Handle<String> replacement =
      isolate->factory()->NewStringFromAsciiChecked("&quot;");
  const int estimated_part_count = static_cast<int>(indices.size()) * 2 + 1;
  ReplacementStringBuilder builder(isolate->heap(), string,
                                   estimated_part_count);

  int prev_index = -1;  // Start at -1 to avoid special-casing the first match.
  for (int index : indices) {
    const int slice_start = prev_index + 1;
    const int slice_end = index;
    if (slice_end > slice_start) {
      builder.AddSubjectSlice(slice_start, slice_end);
    }
    builder.AddString(replacement);
    prev_index = index;
  }

  if (prev_index < string_length - 1) {
    builder.AddSubjectSlice(prev_index + 1, string_length);
  }

  return *builder.ToString().ToHandleChecked();
}

435 436
}  // namespace internal
}  // namespace v8