| 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);
|
| + }
|
| }
|
|
|
|
|
|
|