summaryrefslogtreecommitdiff
path: root/deps/v8/src/json/json-stringifier.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/json/json-stringifier.cc')
-rw-r--r--deps/v8/src/json/json-stringifier.cc1038
1 files changed, 1038 insertions, 0 deletions
diff --git a/deps/v8/src/json/json-stringifier.cc b/deps/v8/src/json/json-stringifier.cc
new file mode 100644
index 0000000000..2280292332
--- /dev/null
+++ b/deps/v8/src/json/json-stringifier.cc
@@ -0,0 +1,1038 @@
+// Copyright 2016 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.
+
+#include "src/json/json-stringifier.h"
+
+#include "src/execution/message-template.h"
+#include "src/numbers/conversions.h"
+#include "src/objects/heap-number-inl.h"
+#include "src/objects/js-array-inl.h"
+#include "src/objects/lookup.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/oddball-inl.h"
+#include "src/objects/ordered-hash-table.h"
+#include "src/objects/smi.h"
+#include "src/strings/string-builder-inl.h"
+#include "src/utils/utils.h"
+
+namespace v8 {
+namespace internal {
+
+class JsonStringifier {
+ public:
+ explicit JsonStringifier(Isolate* isolate);
+
+ ~JsonStringifier() { DeleteArray(gap_); }
+
+ V8_WARN_UNUSED_RESULT MaybeHandle<Object> Stringify(Handle<Object> object,
+ Handle<Object> replacer,
+ Handle<Object> gap);
+
+ private:
+ enum Result { UNCHANGED, SUCCESS, EXCEPTION };
+
+ bool InitializeReplacer(Handle<Object> replacer);
+ bool InitializeGap(Handle<Object> gap);
+
+ V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyToJsonFunction(
+ Handle<Object> object, Handle<Object> key);
+ V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyReplacerFunction(
+ Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder);
+
+ // Entry point to serialize the object.
+ V8_INLINE Result SerializeObject(Handle<Object> obj) {
+ return Serialize_<false>(obj, false, factory()->empty_string());
+ }
+
+ // Serialize an array element.
+ // The index may serve as argument for the toJSON function.
+ V8_INLINE Result SerializeElement(Isolate* isolate, Handle<Object> object,
+ int i) {
+ return Serialize_<false>(object, false,
+ Handle<Object>(Smi::FromInt(i), isolate));
+ }
+
+ // Serialize a object property.
+ // The key may or may not be serialized depending on the property.
+ // The key may also serve as argument for the toJSON function.
+ V8_INLINE Result SerializeProperty(Handle<Object> object, bool deferred_comma,
+ Handle<String> deferred_key) {
+ DCHECK(!deferred_key.is_null());
+ return Serialize_<true>(object, deferred_comma, deferred_key);
+ }
+
+ template <bool deferred_string_key>
+ Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
+
+ V8_INLINE void SerializeDeferredKey(bool deferred_comma,
+ Handle<Object> deferred_key);
+
+ Result SerializeSmi(Smi object);
+
+ Result SerializeDouble(double number);
+ V8_INLINE Result SerializeHeapNumber(Handle<HeapNumber> object) {
+ return SerializeDouble(object->value());
+ }
+
+ Result SerializeJSValue(Handle<JSValue> object, Handle<Object> key);
+
+ V8_INLINE Result SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
+ V8_INLINE Result SerializeJSObject(Handle<JSObject> object,
+ Handle<Object> key);
+
+ Result SerializeJSProxy(Handle<JSProxy> object, Handle<Object> key);
+ Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
+ Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
+ uint32_t length);
+
+ void SerializeString(Handle<String> object);
+
+ template <typename SrcChar, typename DestChar>
+ V8_INLINE static void SerializeStringUnchecked_(
+ Vector<const SrcChar> src,
+ IncrementalStringBuilder::NoExtend<DestChar>* dest);
+
+ template <typename SrcChar, typename DestChar>
+ V8_INLINE void SerializeString_(Handle<String> string);
+
+ template <typename Char>
+ V8_INLINE static bool DoNotEscape(Char c);
+
+ V8_INLINE void NewLine();
+ V8_INLINE void Indent() { indent_++; }
+ V8_INLINE void Unindent() { indent_--; }
+ V8_INLINE void Separator(bool first);
+
+ Handle<JSReceiver> CurrentHolder(Handle<Object> value,
+ Handle<Object> inital_holder);
+
+ Result StackPush(Handle<Object> object, Handle<Object> key);
+ void StackPop();
+
+ // Uses the current stack_ to provide a detailed error message of
+ // the objects involved in the circular structure.
+ Handle<String> ConstructCircularStructureErrorMessage(Handle<Object> last_key,
+ size_t start_index);
+ // The prefix and postfix count do NOT include the starting and
+ // closing lines of the error message.
+ static const int kCircularErrorMessagePrefixCount = 2;
+ static const int kCircularErrorMessagePostfixCount = 1;
+
+ Factory* factory() { return isolate_->factory(); }
+
+ Isolate* isolate_;
+ IncrementalStringBuilder builder_;
+ Handle<String> tojson_string_;
+ Handle<FixedArray> property_list_;
+ Handle<JSReceiver> replacer_function_;
+ uc16* gap_;
+ int indent_;
+
+ using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
+ std::vector<KeyObject> stack_;
+
+ static const int kJsonEscapeTableEntrySize = 8;
+ static const char* const JsonEscapeTable;
+};
+
+MaybeHandle<Object> JsonStringify(Isolate* isolate, Handle<Object> object,
+ Handle<Object> replacer, Handle<Object> gap) {
+ JsonStringifier stringifier(isolate);
+ return stringifier.Stringify(object, replacer, gap);
+}
+
+// Translation table to escape Latin1 characters.
+// Table entries start at a multiple of 8 and are null-terminated.
+const char* const JsonStringifier::JsonEscapeTable =
+ "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
+ "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
+ "\\b\0 \\t\0 \\n\0 \\u000b\0 "
+ "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
+ "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
+ "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
+ "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
+ "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
+ " \0 !\0 \\\"\0 #\0 "
+ "$\0 %\0 &\0 '\0 "
+ "(\0 )\0 *\0 +\0 "
+ ",\0 -\0 .\0 /\0 "
+ "0\0 1\0 2\0 3\0 "
+ "4\0 5\0 6\0 7\0 "
+ "8\0 9\0 :\0 ;\0 "
+ "<\0 =\0 >\0 ?\0 "
+ "@\0 A\0 B\0 C\0 "
+ "D\0 E\0 F\0 G\0 "
+ "H\0 I\0 J\0 K\0 "
+ "L\0 M\0 N\0 O\0 "
+ "P\0 Q\0 R\0 S\0 "
+ "T\0 U\0 V\0 W\0 "
+ "X\0 Y\0 Z\0 [\0 "
+ "\\\\\0 ]\0 ^\0 _\0 "
+ "`\0 a\0 b\0 c\0 "
+ "d\0 e\0 f\0 g\0 "
+ "h\0 i\0 j\0 k\0 "
+ "l\0 m\0 n\0 o\0 "
+ "p\0 q\0 r\0 s\0 "
+ "t\0 u\0 v\0 w\0 "
+ "x\0 y\0 z\0 {\0 "
+ "|\0 }\0 ~\0 \x7F\0 "
+ "\x80\0 \x81\0 \x82\0 \x83\0 "
+ "\x84\0 \x85\0 \x86\0 \x87\0 "
+ "\x88\0 \x89\0 \x8A\0 \x8B\0 "
+ "\x8C\0 \x8D\0 \x8E\0 \x8F\0 "
+ "\x90\0 \x91\0 \x92\0 \x93\0 "
+ "\x94\0 \x95\0 \x96\0 \x97\0 "
+ "\x98\0 \x99\0 \x9A\0 \x9B\0 "
+ "\x9C\0 \x9D\0 \x9E\0 \x9F\0 "
+ "\xA0\0 \xA1\0 \xA2\0 \xA3\0 "
+ "\xA4\0 \xA5\0 \xA6\0 \xA7\0 "
+ "\xA8\0 \xA9\0 \xAA\0 \xAB\0 "
+ "\xAC\0 \xAD\0 \xAE\0 \xAF\0 "
+ "\xB0\0 \xB1\0 \xB2\0 \xB3\0 "
+ "\xB4\0 \xB5\0 \xB6\0 \xB7\0 "
+ "\xB8\0 \xB9\0 \xBA\0 \xBB\0 "
+ "\xBC\0 \xBD\0 \xBE\0 \xBF\0 "
+ "\xC0\0 \xC1\0 \xC2\0 \xC3\0 "
+ "\xC4\0 \xC5\0 \xC6\0 \xC7\0 "
+ "\xC8\0 \xC9\0 \xCA\0 \xCB\0 "
+ "\xCC\0 \xCD\0 \xCE\0 \xCF\0 "
+ "\xD0\0 \xD1\0 \xD2\0 \xD3\0 "
+ "\xD4\0 \xD5\0 \xD6\0 \xD7\0 "
+ "\xD8\0 \xD9\0 \xDA\0 \xDB\0 "
+ "\xDC\0 \xDD\0 \xDE\0 \xDF\0 "
+ "\xE0\0 \xE1\0 \xE2\0 \xE3\0 "
+ "\xE4\0 \xE5\0 \xE6\0 \xE7\0 "
+ "\xE8\0 \xE9\0 \xEA\0 \xEB\0 "
+ "\xEC\0 \xED\0 \xEE\0 \xEF\0 "
+ "\xF0\0 \xF1\0 \xF2\0 \xF3\0 "
+ "\xF4\0 \xF5\0 \xF6\0 \xF7\0 "
+ "\xF8\0 \xF9\0 \xFA\0 \xFB\0 "
+ "\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
+
+JsonStringifier::JsonStringifier(Isolate* isolate)
+ : isolate_(isolate),
+ builder_(isolate),
+ gap_(nullptr),
+ indent_(0),
+ stack_() {
+ tojson_string_ = factory()->toJSON_string();
+}
+
+MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
+ Handle<Object> replacer,
+ Handle<Object> gap) {
+ if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
+ if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) {
+ return MaybeHandle<Object>();
+ }
+ Result result = SerializeObject(object);
+ if (result == UNCHANGED) return factory()->undefined_value();
+ if (result == SUCCESS) return builder_.Finish();
+ DCHECK(result == EXCEPTION);
+ return MaybeHandle<Object>();
+}
+
+bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
+ DCHECK(property_list_.is_null());
+ DCHECK(replacer_function_.is_null());
+ Maybe<bool> is_array = Object::IsArray(replacer);
+ if (is_array.IsNothing()) return false;
+ if (is_array.FromJust()) {
+ HandleScope handle_scope(isolate_);
+ Handle<OrderedHashSet> set = factory()->NewOrderedHashSet();
+ Handle<Object> length_obj;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, length_obj,
+ Object::GetLengthFromArrayLike(isolate_,
+ Handle<JSReceiver>::cast(replacer)),
+ false);
+ uint32_t length;
+ if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
+ for (uint32_t i = 0; i < length; i++) {
+ Handle<Object> element;
+ Handle<String> key;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, element, Object::GetElement(isolate_, replacer, i), false);
+ if (element->IsNumber() || element->IsString()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, key, Object::ToString(isolate_, element), false);
+ } else if (element->IsJSValue()) {
+ Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_);
+ if (value->IsNumber() || value->IsString()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, key, Object::ToString(isolate_, element), false);
+ }
+ }
+ if (key.is_null()) continue;
+ // Object keys are internalized, so do it here.
+ key = factory()->InternalizeString(key);
+ set = OrderedHashSet::Add(isolate_, set, key);
+ }
+ property_list_ = OrderedHashSet::ConvertToKeysArray(
+ isolate_, set, GetKeysConversion::kKeepNumbers);
+ property_list_ = handle_scope.CloseAndEscape(property_list_);
+ } else if (replacer->IsCallable()) {
+ replacer_function_ = Handle<JSReceiver>::cast(replacer);
+ }
+ return true;
+}
+
+bool JsonStringifier::InitializeGap(Handle<Object> gap) {
+ DCHECK_NULL(gap_);
+ HandleScope scope(isolate_);
+ if (gap->IsJSValue()) {
+ Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_);
+ if (value->IsString()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
+ Object::ToString(isolate_, gap), false);
+ } else if (value->IsNumber()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
+ Object::ToNumber(isolate_, gap), false);
+ }
+ }
+
+ if (gap->IsString()) {
+ Handle<String> gap_string = Handle<String>::cast(gap);
+ if (gap_string->length() > 0) {
+ int gap_length = std::min(gap_string->length(), 10);
+ gap_ = NewArray<uc16>(gap_length + 1);
+ String::WriteToFlat(*gap_string, gap_, 0, gap_length);
+ for (int i = 0; i < gap_length; i++) {
+ if (gap_[i] > String::kMaxOneByteCharCode) {
+ builder_.ChangeEncoding();
+ break;
+ }
+ }
+ gap_[gap_length] = '\0';
+ }
+ } else if (gap->IsNumber()) {
+ int num_value = DoubleToInt32(gap->Number());
+ if (num_value > 0) {
+ int gap_length = std::min(num_value, 10);
+ gap_ = NewArray<uc16>(gap_length + 1);
+ for (int i = 0; i < gap_length; i++) gap_[i] = ' ';
+ gap_[gap_length] = '\0';
+ }
+ }
+ return true;
+}
+
+MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object,
+ Handle<Object> key) {
+ HandleScope scope(isolate_);
+
+ // Retrieve toJSON function. The LookupIterator automatically handles
+ // the ToObject() equivalent ("GetRoot") if {object} is a BigInt.
+ Handle<Object> fun;
+ LookupIterator it(isolate_, object, tojson_string_,
+ LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
+ ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object);
+ if (!fun->IsCallable()) return object;
+
+ // Call toJSON function.
+ if (key->IsSmi()) key = factory()->NumberToString(key);
+ Handle<Object> argv[] = {key};
+ ASSIGN_RETURN_ON_EXCEPTION(isolate_, object,
+ Execution::Call(isolate_, fun, object, 1, argv),
+ Object);
+ return scope.CloseAndEscape(object);
+}
+
+MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
+ Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) {
+ HandleScope scope(isolate_);
+ if (key->IsSmi()) key = factory()->NumberToString(key);
+ Handle<Object> argv[] = {key, value};
+ Handle<JSReceiver> holder = CurrentHolder(value, initial_holder);
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate_, value,
+ Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object);
+ return scope.CloseAndEscape(value);
+}
+
+Handle<JSReceiver> JsonStringifier::CurrentHolder(
+ Handle<Object> value, Handle<Object> initial_holder) {
+ if (stack_.empty()) {
+ Handle<JSObject> holder =
+ factory()->NewJSObject(isolate_->object_function());
+ JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
+ initial_holder, NONE);
+ return holder;
+ } else {
+ return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second),
+ isolate_);
+ }
+}
+
+JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object,
+ Handle<Object> key) {
+ StackLimitCheck check(isolate_);
+ if (check.HasOverflowed()) {
+ isolate_->StackOverflow();
+ return EXCEPTION;
+ }
+
+ {
+ DisallowHeapAllocation no_allocation;
+ for (size_t i = 0; i < stack_.size(); ++i) {
+ if (*stack_[i].second == *object) {
+ AllowHeapAllocation allow_to_return_error;
+ Handle<String> circle_description =
+ ConstructCircularStructureErrorMessage(key, i);
+ Handle<Object> error = factory()->NewTypeError(
+ MessageTemplate::kCircularStructure, circle_description);
+ isolate_->Throw(*error);
+ return EXCEPTION;
+ }
+ }
+ }
+ stack_.emplace_back(key, object);
+ return SUCCESS;
+}
+
+void JsonStringifier::StackPop() { stack_.pop_back(); }
+
+class CircularStructureMessageBuilder {
+ public:
+ explicit CircularStructureMessageBuilder(Isolate* isolate)
+ : builder_(isolate) {}
+
+ void AppendStartLine(Handle<Object> start_object) {
+ builder_.AppendCString(kStartPrefix);
+ builder_.AppendCString("starting at object with constructor ");
+ AppendConstructorName(start_object);
+ }
+
+ void AppendNormalLine(Handle<Object> key, Handle<Object> object) {
+ builder_.AppendCString(kLinePrefix);
+ AppendKey(key);
+ builder_.AppendCString(" -> object with constructor ");
+ AppendConstructorName(object);
+ }
+
+ void AppendClosingLine(Handle<Object> closing_key) {
+ builder_.AppendCString(kEndPrefix);
+ AppendKey(closing_key);
+ builder_.AppendCString(" closes the circle");
+ }
+
+ void AppendEllipsis() {
+ builder_.AppendCString(kLinePrefix);
+ builder_.AppendCString("...");
+ }
+
+ MaybeHandle<String> Finish() { return builder_.Finish(); }
+
+ private:
+ void AppendConstructorName(Handle<Object> object) {
+ builder_.AppendCharacter('\'');
+ Handle<String> constructor_name =
+ JSReceiver::GetConstructorName(Handle<JSReceiver>::cast(object));
+ builder_.AppendString(constructor_name);
+ builder_.AppendCharacter('\'');
+ }
+
+ // A key can either be a string, the empty string or a Smi.
+ void AppendKey(Handle<Object> key) {
+ if (key->IsSmi()) {
+ builder_.AppendCString("index ");
+ AppendSmi(Smi::cast(*key));
+ return;
+ }
+
+ CHECK(key->IsString());
+ Handle<String> key_as_string = Handle<String>::cast(key);
+ if (key_as_string->length() == 0) {
+ builder_.AppendCString("<anonymous>");
+ } else {
+ builder_.AppendCString("property '");
+ builder_.AppendString(key_as_string);
+ builder_.AppendCharacter('\'');
+ }
+ }
+
+ void AppendSmi(Smi smi) {
+ static const int kBufferSize = 100;
+ char chars[kBufferSize];
+ Vector<char> buffer(chars, kBufferSize);
+ builder_.AppendCString(IntToCString(smi.value(), buffer));
+ }
+
+ IncrementalStringBuilder builder_;
+ static constexpr const char* kStartPrefix = "\n --> ";
+ static constexpr const char* kEndPrefix = "\n --- ";
+ static constexpr const char* kLinePrefix = "\n | ";
+};
+
+Handle<String> JsonStringifier::ConstructCircularStructureErrorMessage(
+ Handle<Object> last_key, size_t start_index) {
+ DCHECK(start_index < stack_.size());
+ CircularStructureMessageBuilder builder(isolate_);
+
+ // We track the index to be printed next for better readability.
+ size_t index = start_index;
+ const size_t stack_size = stack_.size();
+
+ builder.AppendStartLine(stack_[index++].second);
+
+ // Append a maximum of kCircularErrorMessagePrefixCount normal lines.
+ const size_t prefix_end =
+ std::min(stack_size, index + kCircularErrorMessagePrefixCount);
+ for (; index < prefix_end; ++index) {
+ builder.AppendNormalLine(stack_[index].first, stack_[index].second);
+ }
+
+ // If the circle consists of too many objects, we skip them and just
+ // print an ellipsis.
+ if (stack_size > index + kCircularErrorMessagePostfixCount) {
+ builder.AppendEllipsis();
+ }
+
+ // Since we calculate the postfix lines from the back of the stack,
+ // we have to ensure that lines are not printed twice.
+ index = std::max(index, stack_size - kCircularErrorMessagePostfixCount);
+ for (; index < stack_size; ++index) {
+ builder.AppendNormalLine(stack_[index].first, stack_[index].second);
+ }
+
+ builder.AppendClosingLine(last_key);
+
+ Handle<String> result;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, result, builder.Finish(),
+ factory()->empty_string());
+ return result;
+}
+
+template <bool deferred_string_key>
+JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
+ bool comma,
+ Handle<Object> key) {
+ StackLimitCheck interrupt_check(isolate_);
+ Handle<Object> initial_value = object;
+ if (interrupt_check.InterruptRequested() &&
+ isolate_->stack_guard()->HandleInterrupts().IsException(isolate_)) {
+ return EXCEPTION;
+ }
+ if (object->IsJSReceiver() || object->IsBigInt()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION);
+ }
+ if (!replacer_function_.is_null()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, object, ApplyReplacerFunction(object, key, initial_value),
+ EXCEPTION);
+ }
+
+ if (object->IsSmi()) {
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeSmi(Smi::cast(*object));
+ }
+
+ switch (HeapObject::cast(*object).map().instance_type()) {
+ case HEAP_NUMBER_TYPE:
+ case MUTABLE_HEAP_NUMBER_TYPE:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
+ case BIGINT_TYPE:
+ isolate_->Throw(
+ *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
+ return EXCEPTION;
+ case ODDBALL_TYPE:
+ switch (Oddball::cast(*object).kind()) {
+ case Oddball::kFalse:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ builder_.AppendCString("false");
+ return SUCCESS;
+ case Oddball::kTrue:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ builder_.AppendCString("true");
+ return SUCCESS;
+ case Oddball::kNull:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ builder_.AppendCString("null");
+ return SUCCESS;
+ default:
+ return UNCHANGED;
+ }
+ case JS_ARRAY_TYPE:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeJSArray(Handle<JSArray>::cast(object), key);
+ case JS_VALUE_TYPE:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeJSValue(Handle<JSValue>::cast(object), key);
+ case SYMBOL_TYPE:
+ return UNCHANGED;
+ default:
+ if (object->IsString()) {
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ SerializeString(Handle<String>::cast(object));
+ return SUCCESS;
+ } else {
+ DCHECK(object->IsJSReceiver());
+ if (object->IsCallable()) return UNCHANGED;
+ // Go to slow path for global proxy and objects requiring access checks.
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ if (object->IsJSProxy()) {
+ return SerializeJSProxy(Handle<JSProxy>::cast(object), key);
+ }
+ return SerializeJSObject(Handle<JSObject>::cast(object), key);
+ }
+ }
+
+ UNREACHABLE();
+}
+
+JsonStringifier::Result JsonStringifier::SerializeJSValue(
+ Handle<JSValue> object, Handle<Object> key) {
+ Object raw = object->value();
+ if (raw.IsString()) {
+ Handle<Object> value;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
+ SerializeString(Handle<String>::cast(value));
+ } else if (raw.IsNumber()) {
+ Handle<Object> value;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION);
+ if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
+ SerializeHeapNumber(Handle<HeapNumber>::cast(value));
+ } else if (raw.IsBigInt()) {
+ isolate_->Throw(
+ *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
+ return EXCEPTION;
+ } else if (raw.IsBoolean()) {
+ builder_.AppendCString(raw.IsTrue(isolate_) ? "true" : "false");
+ } else {
+ // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
+ return SerializeJSObject(object, key);
+ }
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeSmi(Smi object) {
+ static const int kBufferSize = 100;
+ char chars[kBufferSize];
+ Vector<char> buffer(chars, kBufferSize);
+ builder_.AppendCString(IntToCString(object.value(), buffer));
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
+ if (std::isinf(number) || std::isnan(number)) {
+ builder_.AppendCString("null");
+ return SUCCESS;
+ }
+ static const int kBufferSize = 100;
+ char chars[kBufferSize];
+ Vector<char> buffer(chars, kBufferSize);
+ builder_.AppendCString(DoubleToCString(number, buffer));
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeJSArray(
+ Handle<JSArray> object, Handle<Object> key) {
+ HandleScope handle_scope(isolate_);
+ Result stack_push = StackPush(object, key);
+ if (stack_push != SUCCESS) return stack_push;
+ uint32_t length = 0;
+ CHECK(object->length().ToArrayLength(&length));
+ DCHECK(!object->IsAccessCheckNeeded());
+ builder_.AppendCharacter('[');
+ Indent();
+ uint32_t i = 0;
+ if (replacer_function_.is_null()) {
+ switch (object->GetElementsKind()) {
+ case PACKED_SMI_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()),
+ isolate_);
+ StackLimitCheck interrupt_check(isolate_);
+ while (i < length) {
+ if (interrupt_check.InterruptRequested() &&
+ isolate_->stack_guard()->HandleInterrupts().IsException(
+ isolate_)) {
+ return EXCEPTION;
+ }
+ Separator(i == 0);
+ SerializeSmi(Smi::cast(elements->get(i)));
+ i++;
+ }
+ break;
+ }
+ case PACKED_DOUBLE_ELEMENTS: {
+ // Empty array is FixedArray but not FixedDoubleArray.
+ if (length == 0) break;
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(object->elements()), isolate_);
+ StackLimitCheck interrupt_check(isolate_);
+ while (i < length) {
+ if (interrupt_check.InterruptRequested() &&
+ isolate_->stack_guard()->HandleInterrupts().IsException(
+ isolate_)) {
+ return EXCEPTION;
+ }
+ Separator(i == 0);
+ SerializeDouble(elements->get_scalar(i));
+ i++;
+ }
+ break;
+ }
+ case PACKED_ELEMENTS: {
+ Handle<Object> old_length(object->length(), isolate_);
+ while (i < length) {
+ if (object->length() != *old_length ||
+ object->GetElementsKind() != PACKED_ELEMENTS) {
+ // Fall back to slow path.
+ break;
+ }
+ Separator(i == 0);
+ Result result = SerializeElement(
+ isolate_,
+ Handle<Object>(FixedArray::cast(object->elements()).get(i),
+ isolate_),
+ i);
+ if (result == UNCHANGED) {
+ builder_.AppendCString("null");
+ } else if (result != SUCCESS) {
+ return result;
+ }
+ i++;
+ }
+ break;
+ }
+ // The FAST_HOLEY_* cases could be handled in a faster way. They resemble
+ // the non-holey cases except that a lookup is necessary for holes.
+ default:
+ break;
+ }
+ }
+ if (i < length) {
+ // Slow path for non-fast elements and fall-back in edge case.
+ Result result = SerializeArrayLikeSlow(object, i, length);
+ if (result != SUCCESS) return result;
+ }
+ Unindent();
+ if (length > 0) NewLine();
+ builder_.AppendCharacter(']');
+ StackPop();
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
+ Handle<JSReceiver> object, uint32_t start, uint32_t length) {
+ // We need to write out at least two characters per array element.
+ static const int kMaxSerializableArrayLength = String::kMaxLength / 2;
+ if (length > kMaxSerializableArrayLength) {
+ isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
+ return EXCEPTION;
+ }
+ for (uint32_t i = start; i < length; i++) {
+ Separator(i == 0);
+ Handle<Object> element;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, element, JSReceiver::GetElement(isolate_, object, i),
+ EXCEPTION);
+ Result result = SerializeElement(isolate_, element, i);
+ if (result == SUCCESS) continue;
+ if (result == UNCHANGED) {
+ // Detect overflow sooner for large sparse arrays.
+ if (builder_.HasOverflowed()) return EXCEPTION;
+ builder_.AppendCString("null");
+ } else {
+ return result;
+ }
+ }
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeJSObject(
+ Handle<JSObject> object, Handle<Object> key) {
+ HandleScope handle_scope(isolate_);
+ Result stack_push = StackPush(object, key);
+ if (stack_push != SUCCESS) return stack_push;
+
+ if (property_list_.is_null() &&
+ !object->map().IsCustomElementsReceiverMap() &&
+ object->HasFastProperties() &&
+ (object->elements() == ReadOnlyRoots(isolate_).empty_fixed_array() ||
+ object->elements() ==
+ ReadOnlyRoots(isolate_).empty_slow_element_dictionary())) {
+ DCHECK(!object->IsJSGlobalProxy());
+ DCHECK(!object->HasIndexedInterceptor());
+ DCHECK(!object->HasNamedInterceptor());
+ Handle<Map> map(object->map(), isolate_);
+ builder_.AppendCharacter('{');
+ Indent();
+ bool comma = false;
+ for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
+ Handle<Name> name(map->instance_descriptors().GetKey(i), isolate_);
+ // TODO(rossberg): Should this throw?
+ if (!name->IsString()) continue;
+ Handle<String> key = Handle<String>::cast(name);
+ PropertyDetails details = map->instance_descriptors().GetDetails(i);
+ if (details.IsDontEnum()) continue;
+ Handle<Object> property;
+ if (details.location() == kField && *map == object->map()) {
+ DCHECK_EQ(kData, details.kind());
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
+ property = JSObject::FastPropertyAt(object, details.representation(),
+ field_index);
+ } else {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, property,
+ Object::GetPropertyOrElement(isolate_, object, key), EXCEPTION);
+ }
+ Result result = SerializeProperty(property, comma, key);
+ if (!comma && result == SUCCESS) comma = true;
+ if (result == EXCEPTION) return result;
+ }
+ Unindent();
+ if (comma) NewLine();
+ builder_.AppendCharacter('}');
+ } else {
+ Result result = SerializeJSReceiverSlow(object);
+ if (result != SUCCESS) return result;
+ }
+ StackPop();
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
+ Handle<JSReceiver> object) {
+ Handle<FixedArray> contents = property_list_;
+ if (contents.is_null()) {
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, contents,
+ KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
+ ENUMERABLE_STRINGS,
+ GetKeysConversion::kConvertToString),
+ EXCEPTION);
+ }
+ builder_.AppendCharacter('{');
+ Indent();
+ bool comma = false;
+ for (int i = 0; i < contents->length(); i++) {
+ Handle<String> key(String::cast(contents->get(i)), isolate_);
+ Handle<Object> property;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, property, Object::GetPropertyOrElement(isolate_, object, key),
+ EXCEPTION);
+ Result result = SerializeProperty(property, comma, key);
+ if (!comma && result == SUCCESS) comma = true;
+ if (result == EXCEPTION) return result;
+ }
+ Unindent();
+ if (comma) NewLine();
+ builder_.AppendCharacter('}');
+ return SUCCESS;
+}
+
+JsonStringifier::Result JsonStringifier::SerializeJSProxy(
+ Handle<JSProxy> object, Handle<Object> key) {
+ HandleScope scope(isolate_);
+ Result stack_push = StackPush(object, key);
+ if (stack_push != SUCCESS) return stack_push;
+ Maybe<bool> is_array = Object::IsArray(object);
+ if (is_array.IsNothing()) return EXCEPTION;
+ if (is_array.FromJust()) {
+ Handle<Object> length_object;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, length_object,
+ Object::GetLengthFromArrayLike(isolate_,
+ Handle<JSReceiver>::cast(object)),
+ EXCEPTION);
+ uint32_t length;
+ if (!length_object->ToUint32(&length)) {
+ // Technically, we need to be able to handle lengths outside the
+ // uint32_t range. However, we would run into string size overflow
+ // if we tried to stringify such an array.
+ isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
+ return EXCEPTION;
+ }
+ builder_.AppendCharacter('[');
+ Indent();
+ Result result = SerializeArrayLikeSlow(object, 0, length);
+ if (result != SUCCESS) return result;
+ Unindent();
+ if (length > 0) NewLine();
+ builder_.AppendCharacter(']');
+ } else {
+ Result result = SerializeJSReceiverSlow(object);
+ if (result != SUCCESS) return result;
+ }
+ StackPop();
+ return SUCCESS;
+}
+
+template <typename SrcChar, typename DestChar>
+void JsonStringifier::SerializeStringUnchecked_(
+ Vector<const SrcChar> src,
+ IncrementalStringBuilder::NoExtend<DestChar>* dest) {
+ // Assert that uc16 character is not truncated down to 8 bit.
+ // The <uc16, char> version of this method must not be called.
+ DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
+ for (int i = 0; i < src.length(); i++) {
+ SrcChar c = src[i];
+ if (DoNotEscape(c)) {
+ dest->Append(c);
+ } else if (c >= 0xD800 && c <= 0xDFFF) {
+ // The current character is a surrogate.
+ if (c <= 0xDBFF) {
+ // The current character is a leading surrogate.
+ if (i + 1 < src.length()) {
+ // There is a next character.
+ SrcChar next = src[i + 1];
+ if (next >= 0xDC00 && next <= 0xDFFF) {
+ // The next character is a trailing surrogate, meaning this is a
+ // surrogate pair.
+ dest->Append(c);
+ dest->Append(next);
+ i++;
+ } else {
+ // The next character is not a trailing surrogate. Thus, the
+ // current character is a lone leading surrogate.
+ dest->AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ dest->AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ // There is no next character. Thus, the current character is a lone
+ // leading surrogate.
+ dest->AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ dest->AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ // The current character is a lone trailing surrogate. (If it had been
+ // preceded by a leading surrogate, we would've ended up in the other
+ // branch earlier on, and the current character would've been handled
+ // as part of the surrogate pair already.)
+ dest->AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ dest->AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
+ }
+ }
+}
+
+template <typename SrcChar, typename DestChar>
+void JsonStringifier::SerializeString_(Handle<String> string) {
+ int length = string->length();
+ builder_.Append<uint8_t, DestChar>('"');
+ // We might be able to fit the whole escaped string in the current string
+ // part, or we might need to allocate.
+ if (int worst_case_length = builder_.EscapedLengthIfCurrentPartFits(length)) {
+ DisallowHeapAllocation no_gc;
+ Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc);
+ IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
+ &builder_, worst_case_length, no_gc);
+ SerializeStringUnchecked_(vector, &no_extend);
+ } else {
+ FlatStringReader reader(isolate_, string);
+ for (int i = 0; i < reader.length(); i++) {
+ SrcChar c = reader.Get<SrcChar>(i);
+ if (DoNotEscape(c)) {
+ builder_.Append<SrcChar, DestChar>(c);
+ } else if (c >= 0xD800 && c <= 0xDFFF) {
+ // The current character is a surrogate.
+ if (c <= 0xDBFF) {
+ // The current character is a leading surrogate.
+ if (i + 1 < reader.length()) {
+ // There is a next character.
+ SrcChar next = reader.Get<SrcChar>(i + 1);
+ if (next >= 0xDC00 && next <= 0xDFFF) {
+ // The next character is a trailing surrogate, meaning this is a
+ // surrogate pair.
+ builder_.Append<SrcChar, DestChar>(c);
+ builder_.Append<SrcChar, DestChar>(next);
+ i++;
+ } else {
+ // The next character is not a trailing surrogate. Thus, the
+ // current character is a lone leading surrogate.
+ builder_.AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ builder_.AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ // There is no next character. Thus, the current character is a
+ // lone leading surrogate.
+ builder_.AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ builder_.AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ // The current character is a lone trailing surrogate. (If it had
+ // been preceded by a leading surrogate, we would've ended up in the
+ // other branch earlier on, and the current character would've been
+ // handled as part of the surrogate pair already.)
+ builder_.AppendCString("\\u");
+ char* const hex = DoubleToRadixCString(c, 16);
+ builder_.AppendCString(hex);
+ DeleteArray(hex);
+ }
+ } else {
+ builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
+ }
+ }
+ }
+ builder_.Append<uint8_t, DestChar>('"');
+}
+
+template <>
+bool JsonStringifier::DoNotEscape(uint8_t c) {
+ // https://tc39.github.io/ecma262/#table-json-single-character-escapes
+ return c >= 0x23 && c <= 0x7E && c != 0x5C;
+}
+
+template <>
+bool JsonStringifier::DoNotEscape(uint16_t c) {
+ // https://tc39.github.io/ecma262/#table-json-single-character-escapes
+ return c >= 0x23 && c != 0x5C && c != 0x7F && (c < 0xD800 || c > 0xDFFF);
+}
+
+void JsonStringifier::NewLine() {
+ if (gap_ == nullptr) return;
+ builder_.AppendCharacter('\n');
+ for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_);
+}
+
+void JsonStringifier::Separator(bool first) {
+ if (!first) builder_.AppendCharacter(',');
+ NewLine();
+}
+
+void JsonStringifier::SerializeDeferredKey(bool deferred_comma,
+ Handle<Object> deferred_key) {
+ Separator(!deferred_comma);
+ SerializeString(Handle<String>::cast(deferred_key));
+ builder_.AppendCharacter(':');
+ if (gap_ != nullptr) builder_.AppendCharacter(' ');
+}
+
+void JsonStringifier::SerializeString(Handle<String> object) {
+ object = String::Flatten(isolate_, object);
+ if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
+ if (String::IsOneByteRepresentationUnderneath(*object)) {
+ SerializeString_<uint8_t, uint8_t>(object);
+ } else {
+ builder_.ChangeEncoding();
+ SerializeString(object);
+ }
+ } else {
+ if (String::IsOneByteRepresentationUnderneath(*object)) {
+ SerializeString_<uint8_t, uc16>(object);
+ } else {
+ SerializeString_<uc16, uc16>(object);
+ }
+ }
+}
+
+} // namespace internal
+} // namespace v8