2014-09-29 07:08:15 +00:00
|
|
|
// 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.
|
|
|
|
|
2018-07-31 11:57:08 +00:00
|
|
|
#ifndef V8_STRING_BUILDER_INL_H_
|
|
|
|
#define V8_STRING_BUILDER_INL_H_
|
2014-11-12 08:04:41 +00:00
|
|
|
|
2015-08-12 07:32:36 +00:00
|
|
|
#include "src/assert-scope.h"
|
2018-07-31 11:57:08 +00:00
|
|
|
#include "src/handles-inl.h"
|
2018-04-09 19:11:22 +00:00
|
|
|
#include "src/heap/factory.h"
|
2015-08-12 07:32:36 +00:00
|
|
|
#include "src/isolate.h"
|
|
|
|
#include "src/objects.h"
|
2018-07-20 11:48:00 +00:00
|
|
|
#include "src/objects/fixed-array.h"
|
2018-07-31 11:57:08 +00:00
|
|
|
#include "src/objects/string-inl.h"
|
2015-08-11 07:34:10 +00:00
|
|
|
#include "src/utils.h"
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
const int kStringBuilderConcatHelperLengthBits = 11;
|
|
|
|
const int kStringBuilderConcatHelperPositionBits = 19;
|
|
|
|
|
|
|
|
typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
|
|
|
|
StringBuilderSubstringLength;
|
|
|
|
typedef BitField<int, kStringBuilderConcatHelperLengthBits,
|
|
|
|
kStringBuilderConcatHelperPositionBits>
|
|
|
|
StringBuilderSubstringPosition;
|
|
|
|
|
|
|
|
template <typename sinkchar>
|
2018-11-27 00:48:42 +00:00
|
|
|
void StringBuilderConcatHelper(String special, sinkchar* sink,
|
2018-11-25 02:24:43 +00:00
|
|
|
FixedArray fixed_array, int array_length);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
// Returns the result length of the concatenation.
|
|
|
|
// On illegal argument, -1 is returned.
|
2018-11-25 02:24:43 +00:00
|
|
|
int StringBuilderConcatLength(int special_length, FixedArray fixed_array,
|
2018-07-20 11:48:00 +00:00
|
|
|
int array_length, bool* one_byte);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
class FixedArrayBuilder {
|
|
|
|
public:
|
2018-07-20 11:48:00 +00:00
|
|
|
explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity);
|
|
|
|
explicit FixedArrayBuilder(Handle<FixedArray> backing_store);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
bool HasCapacity(int elements);
|
|
|
|
void EnsureCapacity(Isolate* isolate, int elements);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
2018-12-25 00:19:47 +00:00
|
|
|
void Add(Object value);
|
2018-11-03 00:13:22 +00:00
|
|
|
void Add(Smi value);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
Handle<FixedArray> array() { return array_; }
|
|
|
|
|
|
|
|
int length() { return length_; }
|
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
int capacity();
|
2014-09-29 07:08:15 +00:00
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
Handle<JSArray> ToJSArray(Handle<JSArray> target_array);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<FixedArray> array_;
|
|
|
|
int length_;
|
|
|
|
bool has_non_smi_elements_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ReplacementStringBuilder {
|
|
|
|
public:
|
|
|
|
ReplacementStringBuilder(Heap* heap, Handle<String> subject,
|
2018-07-20 11:48:00 +00:00
|
|
|
int estimated_part_count);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
|
|
|
|
int to) {
|
2017-10-18 09:06:55 +00:00
|
|
|
DCHECK_GE(from, 0);
|
2014-09-29 07:08:15 +00:00
|
|
|
int length = to - from;
|
2017-10-18 09:06:55 +00:00
|
|
|
DCHECK_GT(length, 0);
|
2014-09-29 07:08:15 +00:00
|
|
|
if (StringBuilderSubstringLength::is_valid(length) &&
|
|
|
|
StringBuilderSubstringPosition::is_valid(from)) {
|
|
|
|
int encoded_slice = StringBuilderSubstringLength::encode(length) |
|
|
|
|
StringBuilderSubstringPosition::encode(from);
|
|
|
|
builder->Add(Smi::FromInt(encoded_slice));
|
|
|
|
} else {
|
|
|
|
// Otherwise encode as two smis.
|
|
|
|
builder->Add(Smi::FromInt(-length));
|
|
|
|
builder->Add(Smi::FromInt(from));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
void EnsureCapacity(int elements);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
void AddSubjectSlice(int from, int to) {
|
|
|
|
AddSubjectSlice(&array_builder_, from, to);
|
|
|
|
IncrementCharacterCount(to - from);
|
|
|
|
}
|
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
void AddString(Handle<String> string);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
2014-11-12 08:04:41 +00:00
|
|
|
MaybeHandle<String> ToString();
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
void IncrementCharacterCount(int by) {
|
|
|
|
if (character_count_ > String::kMaxLength - by) {
|
|
|
|
STATIC_ASSERT(String::kMaxLength < kMaxInt);
|
|
|
|
character_count_ = kMaxInt;
|
|
|
|
} else {
|
|
|
|
character_count_ += by;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-12-25 00:19:47 +00:00
|
|
|
void AddElement(Object element);
|
2014-09-29 07:08:15 +00:00
|
|
|
|
|
|
|
Heap* heap_;
|
|
|
|
FixedArrayBuilder array_builder_;
|
|
|
|
Handle<String> subject_;
|
|
|
|
int character_count_;
|
|
|
|
bool is_one_byte_;
|
|
|
|
};
|
2014-11-12 08:04:41 +00:00
|
|
|
|
|
|
|
class IncrementalStringBuilder {
|
|
|
|
public:
|
|
|
|
explicit IncrementalStringBuilder(Isolate* isolate);
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE String::Encoding CurrentEncoding() { return encoding_; }
|
2014-11-12 08:04:41 +00:00
|
|
|
|
|
|
|
template <typename SrcChar, typename DestChar>
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void Append(SrcChar c);
|
2014-11-12 08:04:41 +00:00
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void AppendCharacter(uint8_t c) {
|
2014-11-12 08:04:41 +00:00
|
|
|
if (encoding_ == String::ONE_BYTE_ENCODING) {
|
|
|
|
Append<uint8_t, uint8_t>(c);
|
|
|
|
} else {
|
|
|
|
Append<uint8_t, uc16>(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void AppendCString(const char* s) {
|
2014-11-12 08:04:41 +00:00
|
|
|
const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
|
|
|
|
if (encoding_ == String::ONE_BYTE_ENCODING) {
|
|
|
|
while (*u != '\0') Append<uint8_t, uint8_t>(*(u++));
|
|
|
|
} else {
|
|
|
|
while (*u != '\0') Append<uint8_t, uc16>(*(u++));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void AppendCString(const uc16* s) {
|
2016-05-20 07:56:49 +00:00
|
|
|
if (encoding_ == String::ONE_BYTE_ENCODING) {
|
|
|
|
while (*s != '\0') Append<uc16, uint8_t>(*(s++));
|
|
|
|
} else {
|
|
|
|
while (*s != '\0') Append<uc16, uc16>(*(s++));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE bool CurrentPartCanFit(int length) {
|
2014-11-12 08:04:41 +00:00
|
|
|
return part_length_ - current_index_ > length;
|
|
|
|
}
|
|
|
|
|
2017-07-26 10:56:11 +00:00
|
|
|
// We make a rough estimate to find out if the current string can be
|
|
|
|
// serialized without allocating a new string part. The worst case length of
|
|
|
|
// an escaped character is 6. Shifting the remaining string length right by 3
|
|
|
|
// is a more pessimistic estimate, but faster to calculate.
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE int EscapedLengthIfCurrentPartFits(int length) {
|
2017-07-26 10:56:11 +00:00
|
|
|
if (length > kMaxPartLength) return 0;
|
|
|
|
STATIC_ASSERT((kMaxPartLength << 3) <= String::kMaxLength);
|
|
|
|
// This shift will not overflow because length is already less than the
|
|
|
|
// maximum part length.
|
|
|
|
int worst_case_length = length << 3;
|
|
|
|
return CurrentPartCanFit(worst_case_length) ? worst_case_length : 0;
|
|
|
|
}
|
|
|
|
|
2014-11-12 08:04:41 +00:00
|
|
|
void AppendString(Handle<String> string);
|
|
|
|
|
|
|
|
MaybeHandle<String> Finish();
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE bool HasOverflowed() const { return overflowed_; }
|
2016-06-14 11:34:17 +00:00
|
|
|
|
2018-07-20 11:48:00 +00:00
|
|
|
int Length() const;
|
Implement new Function.prototype.toString --harmony-function-tostring
For functions declared in source code, the .toString() representation
will be an excerpt of the source code.
* For functions declared with the "function" keyword, the excerpt
starts at the "function" or "async" keyword and ends at the final "}".
The previous behavior would start the excerpt at the "(" of the
parameter list, and prepend a canonical `"function " + name` or
similar, which would discard comments and formatting surrounding the
function's name. Anonymous functions declared as function expressions
no longer get the name "anonymous" in their toString representation.
* For methods, the excerpt starts at the "get", "set", "*" (for
generator methods), or property name, whichever comes first.
Previously, the toString representation for methods would use a
canonical prefix before the "(" of the parameter list. Note that any
"static" keyword is omitted.
* For arrow functions and class declarations, the excerpt is unchanged.
For functions created with the Function, GeneratorFunction, or
AsyncFunction constructors:
* The string separating the parameter text and body text is now
"\n) {\n", where previously it was "\n/*``*/) {\n" or ") {\n".
* At one point, newline normalization was required by the spec here,
but that was removed from the spec, and so this CL does not do it.
Included in this CL is a fix for CreateDynamicFunction parsing. ')'
and '`' characters in the parameter string are no longer disallowed,
and Function("a=function(", "}){") is no longer allowed.
BUG=v8:4958, v8:4230
Review-Url: https://codereview.chromium.org/2156303002
Cr-Commit-Position: refs/heads/master@{#43262}
2017-02-16 20:19:24 +00:00
|
|
|
|
2014-11-12 08:04:41 +00:00
|
|
|
// Change encoding to two-byte.
|
|
|
|
void ChangeEncoding() {
|
|
|
|
DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
|
|
|
|
ShrinkCurrentPart();
|
|
|
|
encoding_ = String::TWO_BYTE_ENCODING;
|
|
|
|
Extend();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename DestChar>
|
|
|
|
class NoExtend {
|
|
|
|
public:
|
2018-11-30 12:40:36 +00:00
|
|
|
NoExtend(Handle<String> string, int offset,
|
|
|
|
const DisallowHeapAllocation& no_gc) {
|
2014-11-12 08:04:41 +00:00
|
|
|
DCHECK(string->IsSeqOneByteString() || string->IsSeqTwoByteString());
|
|
|
|
if (sizeof(DestChar) == 1) {
|
|
|
|
start_ = reinterpret_cast<DestChar*>(
|
2018-11-30 12:40:36 +00:00
|
|
|
Handle<SeqOneByteString>::cast(string)->GetChars(no_gc) + offset);
|
2014-11-12 08:04:41 +00:00
|
|
|
} else {
|
|
|
|
start_ = reinterpret_cast<DestChar*>(
|
2018-11-30 12:40:36 +00:00
|
|
|
Handle<SeqTwoByteString>::cast(string)->GetChars(no_gc) + offset);
|
2014-11-12 08:04:41 +00:00
|
|
|
}
|
|
|
|
cursor_ = start_;
|
|
|
|
}
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void Append(DestChar c) { *(cursor_++) = c; }
|
|
|
|
V8_INLINE void AppendCString(const char* s) {
|
2014-11-12 08:04:41 +00:00
|
|
|
const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
|
|
|
|
while (*u != '\0') Append(*(u++));
|
|
|
|
}
|
|
|
|
|
2014-11-12 08:15:30 +00:00
|
|
|
int written() { return static_cast<int>(cursor_ - start_); }
|
2014-11-12 08:04:41 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
DestChar* start_;
|
|
|
|
DestChar* cursor_;
|
2019-02-13 00:33:17 +00:00
|
|
|
DISALLOW_HEAP_ALLOCATION(no_gc_)
|
2014-11-12 08:04:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename DestChar>
|
|
|
|
class NoExtendString : public NoExtend<DestChar> {
|
|
|
|
public:
|
|
|
|
NoExtendString(Handle<String> string, int required_length)
|
|
|
|
: NoExtend<DestChar>(string, 0), string_(string) {
|
|
|
|
DCHECK(string->length() >= required_length);
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:33:02 +00:00
|
|
|
Handle<String> Finalize() {
|
2014-11-12 08:04:41 +00:00
|
|
|
Handle<SeqString> string = Handle<SeqString>::cast(string_);
|
|
|
|
int length = NoExtend<DestChar>::written();
|
2015-10-26 15:33:02 +00:00
|
|
|
Handle<String> result = SeqString::Truncate(string, length);
|
|
|
|
string_ = Handle<String>();
|
|
|
|
return result;
|
2014-11-12 08:04:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<String> string_;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename DestChar>
|
|
|
|
class NoExtendBuilder : public NoExtend<DestChar> {
|
|
|
|
public:
|
2018-11-30 12:40:36 +00:00
|
|
|
NoExtendBuilder(IncrementalStringBuilder* builder, int required_length,
|
|
|
|
const DisallowHeapAllocation& no_gc)
|
|
|
|
: NoExtend<DestChar>(builder->current_part(), builder->current_index_,
|
|
|
|
no_gc),
|
2014-11-12 08:04:41 +00:00
|
|
|
builder_(builder) {
|
|
|
|
DCHECK(builder->CurrentPartCanFit(required_length));
|
|
|
|
}
|
|
|
|
|
|
|
|
~NoExtendBuilder() {
|
|
|
|
builder_->current_index_ += NoExtend<DestChar>::written();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
IncrementalStringBuilder* builder_;
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Factory* factory() { return isolate_->factory(); }
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE Handle<String> accumulator() { return accumulator_; }
|
2014-11-12 08:04:41 +00:00
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void set_accumulator(Handle<String> string) {
|
2018-10-31 22:52:56 +00:00
|
|
|
*accumulator_.location() = string->ptr();
|
2014-11-12 08:04:41 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE Handle<String> current_part() { return current_part_; }
|
2014-11-12 08:04:41 +00:00
|
|
|
|
2018-06-22 11:19:13 +00:00
|
|
|
V8_INLINE void set_current_part(Handle<String> string) {
|
2018-10-31 22:52:56 +00:00
|
|
|
*current_part_.location() = string->ptr();
|
2014-11-12 08:04:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the current part to the accumulator.
|
2015-06-19 08:31:22 +00:00
|
|
|
void Accumulate(Handle<String> new_part);
|
2014-11-12 08:04:41 +00:00
|
|
|
|
|
|
|
// Finish the current part and allocate a new part.
|
|
|
|
void Extend();
|
|
|
|
|
|
|
|
// Shrink current part to the right size.
|
|
|
|
void ShrinkCurrentPart() {
|
|
|
|
DCHECK(current_index_ < part_length_);
|
|
|
|
set_current_part(SeqString::Truncate(
|
|
|
|
Handle<SeqString>::cast(current_part()), current_index_));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int kInitialPartLength = 32;
|
|
|
|
static const int kMaxPartLength = 16 * 1024;
|
|
|
|
static const int kPartLengthGrowthFactor = 2;
|
|
|
|
|
|
|
|
Isolate* isolate_;
|
|
|
|
String::Encoding encoding_;
|
|
|
|
bool overflowed_;
|
|
|
|
int part_length_;
|
|
|
|
int current_index_;
|
|
|
|
Handle<String> accumulator_;
|
|
|
|
Handle<String> current_part_;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename SrcChar, typename DestChar>
|
|
|
|
void IncrementalStringBuilder::Append(SrcChar c) {
|
|
|
|
DCHECK_EQ(encoding_ == String::ONE_BYTE_ENCODING, sizeof(DestChar) == 1);
|
|
|
|
if (sizeof(DestChar) == 1) {
|
|
|
|
DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
|
|
|
|
SeqOneByteString::cast(*current_part_)
|
|
|
|
->SeqOneByteStringSet(current_index_++, c);
|
|
|
|
} else {
|
|
|
|
DCHECK_EQ(String::TWO_BYTE_ENCODING, encoding_);
|
|
|
|
SeqTwoByteString::cast(*current_part_)
|
|
|
|
->SeqTwoByteStringSet(current_index_++, c);
|
|
|
|
}
|
|
|
|
if (current_index_ == part_length_) Extend();
|
|
|
|
}
|
2015-09-30 13:46:56 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|
2014-09-29 07:08:15 +00:00
|
|
|
|
2018-07-31 11:57:08 +00:00
|
|
|
#endif // V8_STRING_BUILDER_INL_H_
|