Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index 38f77d07137eb06acec574d67d409cdfca073cd1..58f2a5153071eea0bd76ad383b77eae481e41778 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -5171,11 +5171,365 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_URIUnescape) { |
} |
+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) { |
- HandleScope scope(isolate); |
- CONVERT_ARG_HANDLE_CHECKED(String, string, 0); |
+ 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); |
ASSERT(args.length() == 1); |
- return BasicJsonStringifier::StringifyString(isolate, string); |
+ 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); |
+ } |
} |