| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "platform/assert.h" | |
| #include "platform/unicode.h" | |
| #include "vm/double_conversion.h" | |
| #include "vm/json_writer.h" | |
| #include "vm/object.h" | |
| namespace dart { | |
| class MaybeOnStackBuffer { | |
| public: | |
| explicit MaybeOnStackBuffer(intptr_t size) { | |
| if (size > kOnStackBufferCapacity) { | |
| p_ = reinterpret_cast<char*>(malloc(size)); | |
| } else { | |
| p_ = &buffer_[0]; | |
| } | |
| } | |
| ~MaybeOnStackBuffer() { | |
| if (p_ != &buffer_[0]) free(p_); | |
| } | |
| char* p() { return p_; } | |
| private: | |
| static constexpr intptr_t kOnStackBufferCapacity = 4096; | |
| char* p_; | |
| char buffer_[kOnStackBufferCapacity]; | |
| }; | |
| JSONWriter::JSONWriter(intptr_t buf_size) | |
| : open_objects_(0), buffer_(buf_size) {} | |
| void JSONWriter::AppendBytes(const uint8_t* buffer, intptr_t buffer_length) { | |
| buffer_.AddRaw(buffer, buffer_length); | |
| } | |
| static const char base64_digits[65] = | |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
| static const char base64_pad = '='; | |
| void JSONWriter::AppendBytesInBase64(const uint8_t* bytes, intptr_t length) { | |
| ASSERT(bytes != nullptr); | |
| intptr_t odd_bits = length % 3; | |
| intptr_t even_bits = length - odd_bits; | |
| for (intptr_t i = 0; i < even_bits; i += 3) { | |
| intptr_t triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; | |
| buffer_.AddChar(base64_digits[triplet >> 18]); | |
| buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); | |
| buffer_.AddChar(base64_digits[(triplet >> 6) & 63]); | |
| buffer_.AddChar(base64_digits[triplet & 63]); | |
| } | |
| if (odd_bits == 1) { | |
| intptr_t triplet = bytes[even_bits] << 16; | |
| buffer_.AddChar(base64_digits[triplet >> 18]); | |
| buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); | |
| buffer_.AddChar(base64_pad); | |
| buffer_.AddChar(base64_pad); | |
| } else if (odd_bits == 2) { | |
| intptr_t triplet = (bytes[even_bits] << 16) | (bytes[even_bits + 1] << 8); | |
| buffer_.AddChar(base64_digits[triplet >> 18]); | |
| buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); | |
| buffer_.AddChar(base64_digits[(triplet >> 6) & 63]); | |
| buffer_.AddChar(base64_pad); | |
| } | |
| } | |
| void JSONWriter::AppendSerializedObject(const char* serialized_object) { | |
| PrintCommaIfNeeded(); | |
| buffer_.AddString(serialized_object); | |
| } | |
| void JSONWriter::AppendSerializedObject(const char* property_name, | |
| const char* serialized_object) { | |
| PrintCommaIfNeeded(); | |
| PrintPropertyName(property_name); | |
| buffer_.AddString(serialized_object); | |
| } | |
| void JSONWriter::Clear() { | |
| buffer_.Clear(); | |
| open_objects_ = 0; | |
| } | |
| void JSONWriter::OpenObject(const char* property_name) { | |
| PrintCommaIfNeeded(); | |
| open_objects_++; | |
| if (property_name != nullptr) { | |
| PrintPropertyName(property_name); | |
| } | |
| buffer_.AddChar('{'); | |
| } | |
| void JSONWriter::UncloseObject() { | |
| intptr_t len = buffer_.length(); | |
| ASSERT(len > 0); | |
| ASSERT(buffer_.buffer()[len - 1] == '}'); | |
| open_objects_++; | |
| buffer_.set_length(len - 1); | |
| } | |
| void JSONWriter::CloseObject() { | |
| ASSERT(open_objects_ > 0); | |
| open_objects_--; | |
| buffer_.AddChar('}'); | |
| } | |
| void JSONWriter::OpenArray(const char* property_name) { | |
| PrintCommaIfNeeded(); | |
| if (property_name != nullptr) { | |
| PrintPropertyName(property_name); | |
| } | |
| open_objects_++; | |
| buffer_.AddChar('['); | |
| } | |
| void JSONWriter::CloseArray() { | |
| ASSERT(open_objects_ > 0); | |
| open_objects_--; | |
| buffer_.AddChar(']'); | |
| } | |
| void JSONWriter::PrintValueNull() { | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("null"); | |
| } | |
| void JSONWriter::PrintValueBool(bool b) { | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("%s", b ? "true" : "false"); | |
| } | |
| void JSONWriter::PrintValue(intptr_t i) { | |
| EnsureIntegerIsRepresentableInJavaScript(static_cast<int64_t>(i)); | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("%" Pd "", i); | |
| } | |
| void JSONWriter::PrintValue64(int64_t i) { | |
| EnsureIntegerIsRepresentableInJavaScript(i); | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("%" Pd64 "", i); | |
| } | |
| void JSONWriter::PrintValue(double d) { | |
| // Max length of a double in characters (including \0). | |
| // See double_conversion.cc. | |
| const size_t kBufferLen = 25; | |
| char buffer[kBufferLen]; | |
| DoubleToCString(d, buffer, kBufferLen); | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("%s", buffer); | |
| } | |
| void JSONWriter::PrintValueBase64(const uint8_t* bytes, intptr_t length) { | |
| PrintCommaIfNeeded(); | |
| buffer_.AddChar('"'); | |
| AppendBytesInBase64(bytes, length); | |
| buffer_.AddChar('"'); | |
| } | |
| void JSONWriter::PrintValue(const char* s) { | |
| PrintCommaIfNeeded(); | |
| buffer_.AddChar('"'); | |
| AddEscapedUTF8String(s); | |
| buffer_.AddChar('"'); | |
| } | |
| void JSONWriter::PrintValue(const char* s, intptr_t i) { | |
| PrintCommaIfNeeded(); | |
| buffer_.AddChar('"'); | |
| AddEscapedUTF8String(s, i); | |
| buffer_.AddChar('"'); | |
| } | |
| bool JSONWriter::PrintValueStr(const String& s, | |
| intptr_t offset, | |
| intptr_t count) { | |
| PrintCommaIfNeeded(); | |
| buffer_.AddChar('"'); | |
| bool did_truncate = AddDartString(s, offset, count); | |
| buffer_.AddChar('"'); | |
| return did_truncate; | |
| } | |
| void JSONWriter::PrintValueNoEscape(const char* s) { | |
| PrintCommaIfNeeded(); | |
| buffer_.Printf("%s", s); | |
| } | |
| void JSONWriter::PrintfValue(const char* format, ...) { | |
| va_list args; | |
| va_start(args, format); | |
| VPrintfValue(format, args); | |
| va_end(args); | |
| } | |
| void JSONWriter::VPrintfValue(const char* format, va_list args) { | |
| PrintCommaIfNeeded(); | |
| va_list measure_args; | |
| va_copy(measure_args, args); | |
| intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); | |
| va_end(measure_args); | |
| MaybeOnStackBuffer mosb(len + 1); | |
| char* p = mosb.p(); | |
| va_list print_args; | |
| va_copy(print_args, args); | |
| intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args); | |
| va_end(print_args); | |
| ASSERT(len == len2); | |
| buffer_.AddChar('"'); | |
| AddEscapedUTF8String(p, len); | |
| buffer_.AddChar('"'); | |
| } | |
| void JSONWriter::PrintPropertyBool(const char* name, bool b) { | |
| PrintPropertyName(name); | |
| PrintValueBool(b); | |
| } | |
| void JSONWriter::PrintProperty(const char* name, intptr_t i) { | |
| PrintPropertyName(name); | |
| PrintValue(i); | |
| } | |
| void JSONWriter::PrintProperty64(const char* name, int64_t i) { | |
| PrintPropertyName(name); | |
| PrintValue64(i); | |
| } | |
| void JSONWriter::PrintProperty(const char* name, double d) { | |
| PrintPropertyName(name); | |
| PrintValue(d); | |
| } | |
| void JSONWriter::PrintProperty(const char* name, const char* s) { | |
| PrintPropertyName(name); | |
| PrintValue(s); | |
| } | |
| void JSONWriter::PrintPropertyBase64(const char* name, | |
| const uint8_t* b, | |
| intptr_t len) { | |
| PrintPropertyName(name); | |
| PrintValueBase64(b, len); | |
| } | |
| bool JSONWriter::PrintPropertyStr(const char* name, | |
| const String& s, | |
| intptr_t offset, | |
| intptr_t count) { | |
| PrintPropertyName(name); | |
| return PrintValueStr(s, offset, count); | |
| } | |
| void JSONWriter::PrintPropertyNoEscape(const char* name, const char* s) { | |
| PrintPropertyName(name); | |
| PrintValueNoEscape(s); | |
| } | |
| void JSONWriter::PrintfProperty(const char* name, const char* format, ...) { | |
| va_list args; | |
| va_start(args, format); | |
| VPrintfProperty(name, format, args); | |
| va_end(args); | |
| } | |
| void JSONWriter::VPrintfProperty(const char* name, | |
| const char* format, | |
| va_list args) { | |
| PrintPropertyName(name); | |
| va_list measure_args; | |
| va_copy(measure_args, args); | |
| intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); | |
| va_end(measure_args); | |
| MaybeOnStackBuffer mosb(len + 1); | |
| char* p = mosb.p(); | |
| va_list print_args; | |
| va_copy(print_args, args); | |
| intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args); | |
| va_end(print_args); | |
| ASSERT(len == len2); | |
| buffer_.AddChar('"'); | |
| AddEscapedUTF8String(p, len); | |
| buffer_.AddChar('"'); | |
| } | |
| void JSONWriter::Steal(char** buffer, intptr_t* buffer_length) { | |
| ASSERT(buffer != nullptr); | |
| ASSERT(buffer_length != nullptr); | |
| *buffer_length = buffer_.length(); | |
| *buffer = buffer_.Steal(); | |
| } | |
| void JSONWriter::PrintPropertyName(const char* name) { | |
| ASSERT(name != nullptr); | |
| PrintCommaIfNeeded(); | |
| buffer_.AddChar('"'); | |
| AddEscapedUTF8String(name); | |
| buffer_.AddChar('"'); | |
| buffer_.AddChar(':'); | |
| } | |
| void JSONWriter::PrintNewline() { | |
| buffer_.AddChar('\n'); | |
| } | |
| void JSONWriter::PrintCommaIfNeeded() { | |
| if (NeedComma()) { | |
| buffer_.AddChar(','); | |
| } | |
| } | |
| bool JSONWriter::NeedComma() { | |
| const char* buffer = buffer_.buffer(); | |
| intptr_t length = buffer_.length(); | |
| if (length == 0) { | |
| return false; | |
| } | |
| char ch = buffer[length - 1]; | |
| return (ch != '[') && (ch != '{') && (ch != ':') && (ch != ','); | |
| } | |
| void JSONWriter::EnsureIntegerIsRepresentableInJavaScript(int64_t i) { | |
| #ifdef DEBUG | |
| if (!Utils::IsJavaScriptInt(i)) { | |
| OS::PrintErr( | |
| "JSONWriter::EnsureIntegerIsRepresentableInJavaScript failed on " | |
| "%" Pd64 "\n", | |
| i); | |
| UNREACHABLE(); | |
| } | |
| #endif | |
| } | |
| void JSONWriter::AddEscapedUTF8String(const char* s) { | |
| if (s == nullptr) { | |
| return; | |
| } | |
| intptr_t len = strlen(s); | |
| AddEscapedUTF8String(s, len); | |
| } | |
| void JSONWriter::AddEscapedUTF8String(const char* s, intptr_t len) { | |
| if (s == nullptr) { | |
| return; | |
| } | |
| buffer_.AddEscapedUTF8(s, len); | |
| } | |
| bool JSONWriter::AddDartString(const String& s, | |
| intptr_t offset, | |
| intptr_t count) { | |
| intptr_t length = s.Length(); | |
| ASSERT(offset >= 0); | |
| if (offset > length) { | |
| offset = length; | |
| } | |
| if (!Utils::RangeCheck(offset, count, length)) { | |
| count = length - offset; | |
| } | |
| if (count > 0) { // Avoid asserts about harmless out-of-bounds index. | |
| NoSafepointScope no_safepoint; | |
| if (s.IsOneByteString()) { | |
| buffer_.AddEscapedLatin1(OneByteString::CharAddr(s, offset), count); | |
| } else if (s.IsTwoByteString()) { | |
| buffer_.AddEscapedUTF16(TwoByteString::CharAddr(s, offset), count); | |
| } else { | |
| UNREACHABLE(); | |
| } | |
| } | |
| // Return value indicates whether the string is truncated. | |
| intptr_t limit = offset + count; | |
| return (offset > 0) || (limit < length); | |
| } | |
| } // namespace dart |