MaybeObject* Stringify(Handle<Object> object);
+ INLINE(static MaybeObject* StringifyString(Isolate* isolate,
+ Handle<String> object));
+
private:
static const int kInitialPartLength = 32;
static const int kMaxPartLength = 16 * 1024;
bool deferred_comma,
bool deferred_key);
+ template <typename StringType>
+ INLINE(static MaybeObject* StringifyString_(Isolate* isolate,
+ Handle<String> string,
+ Handle<String> result));
+
// Entry point to serialize the object.
INLINE(Result SerializeObject(Handle<Object> obj)) {
return Serialize_<false>(obj, false, factory_->empty_string());
void SerializeString(Handle<String> object);
template <typename SrcChar, typename DestChar>
- INLINE(void SerializeStringUnchecked_(const SrcChar* src,
- DestChar* dest,
- int length));
+ INLINE(static int SerializeStringUnchecked_(const SrcChar* src,
+ DestChar* dest,
+ int length));
template <bool is_ascii, typename Char>
INLINE(void SerializeString_(Handle<String> string));
template <typename Char>
- INLINE(bool DoNotEscape(Char c));
+ INLINE(static bool DoNotEscape(Char c));
template <typename Char>
- INLINE(Vector<const Char> GetCharVector(Handle<String> string));
+ INLINE(static Vector<const Char> GetCharVector(Handle<String> string));
Result StackPush(Handle<Object> object);
void StackPop();
"\370\0 \371\0 \372\0 \373\0 "
"\374\0 \375\0 \376\0 \377\0 ";
+
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
: isolate_(isolate), current_index_(0), is_ascii_(true) {
factory_ = isolate_->factory();
accumulator_store_ = Handle<JSValue>::cast(
factory_->ToObject(factory_->empty_string()));
part_length_ = kInitialPartLength;
- current_part_ = factory_->NewRawOneByteString(kInitialPartLength);
- tojson_string_ =
- factory_->InternalizeOneByteString(STATIC_ASCII_VECTOR("toJSON"));
+ current_part_ = factory_->NewRawOneByteString(part_length_);
+ tojson_string_ = factory_->toJSON_string();
stack_ = factory_->NewJSArray(8);
}
}
+MaybeObject* BasicJsonStringifier::StringifyString(Isolate* isolate,
+ Handle<String> object) {
+ static const int kJsonQuoteWorstCaseBlowup = 6;
+ static const int kSpaceForQuotes = 2;
+ int worst_case_length =
+ object->length() * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
+
+ if (worst_case_length > 32 * KB) { // Slow path if too large.
+ BasicJsonStringifier stringifier(isolate);
+ return stringifier.Stringify(object);
+ }
+
+ object = FlattenGetString(object);
+ if (object->IsSeqOneByteString()) {
+ return StringifyString_<SeqOneByteString>(
+ isolate,
+ object,
+ isolate->factory()->NewRawOneByteString(worst_case_length));
+ } else {
+ return StringifyString_<SeqTwoByteString>(
+ isolate,
+ object,
+ isolate->factory()->NewRawTwoByteString(worst_case_length));
+ }
+}
+
+
+template <typename StringType>
+MaybeObject* BasicJsonStringifier::StringifyString_(Isolate* isolate,
+ Handle<String> string,
+ Handle<String> result) {
+ AssertNoAllocation no_allocation;
+ int final_size = 0;
+ StringType* dest = StringType::cast(*result);
+ dest->Set(final_size++, '\"');
+ final_size += SerializeStringUnchecked_(StringType::cast(*string)->GetChars(),
+ dest->GetChars() + 1,
+ string->length());
+ dest->Set(final_size++, '\"');
+ if (isolate->heap()->InNewSpace(*result)) {
+ // In new space, simply lower the allocation top to fit the actual size.
+ isolate->heap()->new_space()->ShrinkStringAtAllocationBoundary<StringType>(
+ *result, final_size);
+ return *result;
+ } else {
+ // Not in new space, need to fill the wasted space with filler objects.
+ return SeqString::cast(*result)->Truncate(final_size);
+ }
+}
+
+
template <bool is_ascii, typename Char>
void BasicJsonStringifier::Append_(Char c) {
if (is_ascii) {
template <typename SrcChar, typename DestChar>
-void BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
- DestChar* dest,
- int length) {
- dest += current_index_;
+int BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
+ DestChar* dest,
+ int length) {
DestChar* dest_start = dest;
// Assert that uc16 character is not truncated down to 8 bit.
}
}
- current_index_ += static_cast<int>(dest - dest_start);
+ return static_cast<int>(dest - dest_start);
}
AssertNoAllocation no_allocation;
Vector<const Char> vector = GetCharVector<Char>(string);
if (is_ascii) {
- SerializeStringUnchecked_(
+ current_index_ += SerializeStringUnchecked_(
vector.start(),
- SeqOneByteString::cast(*current_part_)->GetChars(),
+ SeqOneByteString::cast(*current_part_)->GetChars() + current_index_,
length);
} else {
- SerializeStringUnchecked_(
+ current_index_ += SerializeStringUnchecked_(
vector.start(),
- SeqTwoByteString::cast(*current_part_)->GetChars(),
+ SeqTwoByteString::cast(*current_part_)->GetChars() + current_index_,
length);
}
} else {
}
-static const unsigned int kQuoteTableLength = 128u;
-
-static const int kJsonQuotesCharactersPerEntry = 8;
-static const char* const JsonQuotes =
- "\\u0000 \\u0001 \\u0002 \\u0003 "
- "\\u0004 \\u0005 \\u0006 \\u0007 "
- "\\b \\t \\n \\u000b "
- "\\f \\r \\u000e \\u000f "
- "\\u0010 \\u0011 \\u0012 \\u0013 "
- "\\u0014 \\u0015 \\u0016 \\u0017 "
- "\\u0018 \\u0019 \\u001a \\u001b "
- "\\u001c \\u001d \\u001e \\u001f "
- " ! \\\" # "
- "$ % & ' "
- "( ) * + "
- ", - . / "
- "0 1 2 3 "
- "4 5 6 7 "
- "8 9 : ; "
- "< = > ? "
- "@ A B C "
- "D E F G "
- "H I J K "
- "L M N O "
- "P Q R S "
- "T U V W "
- "X Y Z [ "
- "\\\\ ] ^ _ "
- "` a b c "
- "d e f g "
- "h i j k "
- "l m n o "
- "p q r s "
- "t u v w "
- "x y z { "
- "| } ~ \177 ";
-
-
-// For a string that is less than 32k characters it should always be
-// possible to allocate it in new space.
-static const int kMaxGuaranteedNewSpaceString = 32 * 1024;
-
-
-// Doing JSON quoting cannot make the string more than this many times larger.
-static const int kJsonQuoteWorstCaseBlowup = 6;
-
-static const int kSpaceForQuotesAndComma = 3;
-static const int kSpaceForBrackets = 2;
-
-// Covers the entire ASCII range (all other characters are unchanged by JSON
-// quoting).
-static const byte JsonQuoteLengths[kQuoteTableLength] = {
- 6, 6, 6, 6, 6, 6, 6, 6,
- 2, 2, 2, 6, 2, 2, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 1, 1, 2, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 2, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
-};
-
-
-template <typename StringType>
-MaybeObject* AllocateRawString(Isolate* isolate, int length);
-
-
-template <>
-MaybeObject* AllocateRawString<SeqTwoByteString>(Isolate* isolate, int length) {
- return isolate->heap()->AllocateRawTwoByteString(length);
-}
-
-
-template <>
-MaybeObject* AllocateRawString<SeqOneByteString>(Isolate* isolate, int length) {
- return isolate->heap()->AllocateRawOneByteString(length);
-}
-
-
-template <typename Char, typename StringType, bool comma>
-static MaybeObject* SlowQuoteJsonString(Isolate* isolate,
- Vector<const Char> characters) {
- int length = characters.length();
- const Char* read_cursor = characters.start();
- const Char* end = read_cursor + length;
- const int kSpaceForQuotes = 2 + (comma ? 1 :0);
- int quoted_length = kSpaceForQuotes;
- while (read_cursor < end) {
- Char c = *(read_cursor++);
- if (static_cast<unsigned>(c) >= kQuoteTableLength) {
- quoted_length++;
- } else {
- quoted_length += JsonQuoteLengths[static_cast<unsigned>(c)];
- }
- }
- MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
- quoted_length);
- Object* new_object;
- if (!new_alloc->ToObject(&new_object)) {
- return new_alloc;
- }
- StringType* new_string = StringType::cast(new_object);
-
- Char* write_cursor = reinterpret_cast<Char*>(
- new_string->address() + SeqString::kHeaderSize);
- if (comma) *(write_cursor++) = ',';
- *(write_cursor++) = '"';
-
- read_cursor = characters.start();
- while (read_cursor < end) {
- Char c = *(read_cursor++);
- if (static_cast<unsigned>(c) >= kQuoteTableLength) {
- *(write_cursor++) = c;
- } else {
- int len = JsonQuoteLengths[static_cast<unsigned>(c)];
- const char* replacement = JsonQuotes +
- static_cast<unsigned>(c) * kJsonQuotesCharactersPerEntry;
- for (int i = 0; i < len; i++) {
- *write_cursor++ = *replacement++;
- }
- }
- }
- *(write_cursor++) = '"';
- return new_string;
-}
-
-
-template <typename SinkChar, typename SourceChar>
-static inline SinkChar* WriteQuoteJsonString(
- Isolate* isolate,
- SinkChar* write_cursor,
- Vector<const SourceChar> characters) {
- // SinkChar is only char if SourceChar is guaranteed to be char.
- ASSERT(sizeof(SinkChar) >= sizeof(SourceChar));
- const SourceChar* read_cursor = characters.start();
- const SourceChar* end = read_cursor + characters.length();
- *(write_cursor++) = '"';
- while (read_cursor < end) {
- SourceChar c = *(read_cursor++);
- if (static_cast<unsigned>(c) >= kQuoteTableLength) {
- *(write_cursor++) = static_cast<SinkChar>(c);
- } else {
- int len = JsonQuoteLengths[static_cast<unsigned>(c)];
- const char* replacement = JsonQuotes +
- static_cast<unsigned>(c) * kJsonQuotesCharactersPerEntry;
- write_cursor[0] = replacement[0];
- if (len > 1) {
- write_cursor[1] = replacement[1];
- if (len > 2) {
- ASSERT(len == 6);
- write_cursor[2] = replacement[2];
- write_cursor[3] = replacement[3];
- write_cursor[4] = replacement[4];
- write_cursor[5] = replacement[5];
- }
- }
- write_cursor += len;
- }
- }
- *(write_cursor++) = '"';
- return write_cursor;
-}
-
-
-template <typename Char, typename StringType, bool comma>
-static MaybeObject* QuoteJsonString(Isolate* isolate,
- Vector<const Char> characters) {
- int length = characters.length();
- isolate->counters()->quote_json_char_count()->Increment(length);
- int worst_case_length =
- length * kJsonQuoteWorstCaseBlowup + kSpaceForQuotesAndComma;
- if (worst_case_length > kMaxGuaranteedNewSpaceString) {
- return SlowQuoteJsonString<Char, StringType, comma>(isolate, characters);
- }
-
- MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
- worst_case_length);
- Object* new_object;
- if (!new_alloc->ToObject(&new_object)) {
- return new_alloc;
- }
- if (!isolate->heap()->new_space()->Contains(new_object)) {
- // Even if our string is small enough to fit in new space we still have to
- // handle it being allocated in old space as may happen in the third
- // attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
- // CEntryStub::GenerateCore.
- return SlowQuoteJsonString<Char, StringType, comma>(isolate, characters);
- }
- StringType* new_string = StringType::cast(new_object);
- ASSERT(isolate->heap()->new_space()->Contains(new_string));
-
- Char* write_cursor = reinterpret_cast<Char*>(
- new_string->address() + SeqString::kHeaderSize);
- if (comma) *(write_cursor++) = ',';
- write_cursor = WriteQuoteJsonString<Char, Char>(isolate,
- write_cursor,
- characters);
- int final_length = static_cast<int>(
- write_cursor - reinterpret_cast<Char*>(
- new_string->address() + SeqString::kHeaderSize));
- isolate->heap()->new_space()->
- template ShrinkStringAtAllocationBoundary<StringType>(
- new_string, final_length);
- return new_string;
-}
-
-
RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONString) {
- NoHandleAllocation ha(isolate);
- CONVERT_ARG_CHECKED(String, str, 0);
- if (!str->IsFlat()) {
- MaybeObject* try_flatten = str->TryFlatten();
- Object* flat;
- if (!try_flatten->ToObject(&flat)) {
- return try_flatten;
- }
- str = String::cast(flat);
- ASSERT(str->IsFlat());
- }
- String::FlatContent flat = str->GetFlatContent();
- ASSERT(flat.IsFlat());
- if (flat.IsTwoByte()) {
- return QuoteJsonString<uc16, SeqTwoByteString, false>(isolate,
- flat.ToUC16Vector());
- } else {
- return QuoteJsonString<uint8_t, SeqOneByteString, false>(
- isolate,
- flat.ToOneByteVector());
- }
-}
-
-
-RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringComma) {
- NoHandleAllocation ha(isolate);
- CONVERT_ARG_CHECKED(String, str, 0);
- if (!str->IsFlat()) {
- MaybeObject* try_flatten = str->TryFlatten();
- Object* flat;
- if (!try_flatten->ToObject(&flat)) {
- return try_flatten;
- }
- str = String::cast(flat);
- ASSERT(str->IsFlat());
- }
- String::FlatContent flat = str->GetFlatContent();
- if (flat.IsTwoByte()) {
- return QuoteJsonString<uc16, SeqTwoByteString, true>(isolate,
- flat.ToUC16Vector());
- } else {
- return QuoteJsonString<uint8_t, SeqOneByteString, true>(
- isolate,
- flat.ToOneByteVector());
- }
-}
-
-
-template <typename Char, typename StringType>
-static MaybeObject* QuoteJsonStringArray(Isolate* isolate,
- FixedArray* array,
- int worst_case_length) {
- int length = array->length();
-
- MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
- worst_case_length);
- Object* new_object;
- if (!new_alloc->ToObject(&new_object)) {
- return new_alloc;
- }
- if (!isolate->heap()->new_space()->Contains(new_object)) {
- // Even if our string is small enough to fit in new space we still have to
- // handle it being allocated in old space as may happen in the third
- // attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
- // CEntryStub::GenerateCore.
- return isolate->heap()->undefined_value();
- }
- AssertNoAllocation no_gc;
- StringType* new_string = StringType::cast(new_object);
- ASSERT(isolate->heap()->new_space()->Contains(new_string));
-
- Char* write_cursor = reinterpret_cast<Char*>(
- new_string->address() + SeqString::kHeaderSize);
- *(write_cursor++) = '[';
- for (int i = 0; i < length; i++) {
- if (i != 0) *(write_cursor++) = ',';
- String* str = String::cast(array->get(i));
- String::FlatContent content = str->GetFlatContent();
- ASSERT(content.IsFlat());
- if (content.IsTwoByte()) {
- write_cursor = WriteQuoteJsonString<Char, uc16>(isolate,
- write_cursor,
- content.ToUC16Vector());
- } else {
- write_cursor =
- WriteQuoteJsonString<Char, uint8_t>(isolate,
- write_cursor,
- content.ToOneByteVector());
- }
- }
- *(write_cursor++) = ']';
-
- int final_length = static_cast<int>(
- write_cursor - reinterpret_cast<Char*>(
- new_string->address() + SeqString::kHeaderSize));
- isolate->heap()->new_space()->
- template ShrinkStringAtAllocationBoundary<StringType>(
- new_string, final_length);
- return new_string;
-}
-
-
-RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringArray) {
- NoHandleAllocation ha(isolate);
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
ASSERT(args.length() == 1);
- CONVERT_ARG_CHECKED(JSArray, array, 0);
-
- if (!array->HasFastObjectElements()) {
- return isolate->heap()->undefined_value();
- }
- FixedArray* elements = FixedArray::cast(array->elements());
- int n = elements->length();
- bool ascii = true;
- int total_length = 0;
-
- for (int i = 0; i < n; i++) {
- Object* elt = elements->get(i);
- if (!elt->IsString()) return isolate->heap()->undefined_value();
- String* element = String::cast(elt);
- if (!element->IsFlat()) return isolate->heap()->undefined_value();
- total_length += element->length();
- if (ascii && element->IsTwoByteRepresentation()) {
- ascii = false;
- }
- }
-
- int worst_case_length =
- kSpaceForBrackets + n * kSpaceForQuotesAndComma
- + total_length * kJsonQuoteWorstCaseBlowup;
-
- if (worst_case_length > kMaxGuaranteedNewSpaceString) {
- return isolate->heap()->undefined_value();
- }
-
- if (ascii) {
- return QuoteJsonStringArray<char, SeqOneByteString>(isolate,
- elements,
- worst_case_length);
- } else {
- return QuoteJsonStringArray<uc16, SeqTwoByteString>(isolate,
- elements,
- worst_case_length);
- }
+ return BasicJsonStringifier::StringifyString(isolate, string);
}