/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vm/JSONPrinter.h" #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" #include "mozilla/IntegerPrintfMacros.h" #include #include "jsnum.h" using namespace js; void JSONPrinter::indent() { MOZ_ASSERT(indentLevel_ >= 0); if (indent_) { out_.putChar('\n'); for (int i = 0; i < indentLevel_; i++) { out_.put(" "); } } } void JSONPrinter::propertyName(const char* name) { if (!first_) { out_.putChar(','); } indent(); out_.printf("\"%s\":", name); if (indent_) { out_.put(" "); } first_ = false; } void JSONPrinter::beginObject() { if (!first_) { out_.putChar(','); } indent(); out_.putChar('{'); indentLevel_++; first_ = true; } void JSONPrinter::beginList() { if (!first_) { out_.putChar(','); } indent(); out_.putChar('['); indentLevel_++; first_ = true; } void JSONPrinter::beginObjectProperty(const char* name) { propertyName(name); out_.putChar('{'); indentLevel_++; first_ = true; } void JSONPrinter::beginListProperty(const char* name) { propertyName(name); out_.putChar('['); indentLevel_++; first_ = true; } GenericPrinter& JSONPrinter::beginStringProperty(const char* name) { propertyName(name); out_.putChar('"'); return out_; } void JSONPrinter::endStringProperty() { endString(); first_ = false; } GenericPrinter& JSONPrinter::beginString() { if (!first_) { out_.putChar(','); } indent(); out_.putChar('"'); return out_; } void JSONPrinter::endString() { out_.putChar('"'); } void JSONPrinter::boolProperty(const char* name, bool value) { propertyName(name); out_.put(value ? "true" : "false"); } template static void JSONString(GenericPrinter& out, const CharT* s, size_t length) { const CharT* end = s + length; for (const CharT* t = s; t < end; s = ++t) { // This quote implementation is probably correct, // but uses \u even when not strictly necessary. char16_t c = *t; if (c == '"' || c == '\\') { out.printf("\\"); out.printf("%c", char(c)); } else if (!IsAsciiPrintable(c)) { out.printf("\\u%04x", c); } else { out.printf("%c", char(c)); } } } void JSONPrinter::property(const char* name, JSLinearString* str) { JS::AutoCheckCannotGC nogc; beginStringProperty(name); // Limit the string length to reduce the JSON file size. size_t length = std::min(str->length(), size_t(128)); if (str->hasLatin1Chars()) { JSONString(out_, str->latin1Chars(nogc), length); } else { JSONString(out_, str->twoByteChars(nogc), length); } endStringProperty(); } void JSONPrinter::property(const char* name, const char* value) { beginStringProperty(name); out_.put(value); endStringProperty(); } void JSONPrinter::formatProperty(const char* name, const char* format, ...) { va_list ap; va_start(ap, format); beginStringProperty(name); out_.vprintf(format, ap); endStringProperty(); va_end(ap); } void JSONPrinter::formatProperty(const char* name, const char* format, va_list ap) { beginStringProperty(name); out_.vprintf(format, ap); endStringProperty(); } void JSONPrinter::value(const char* format, ...) { va_list ap; va_start(ap, format); if (!first_) { out_.putChar(','); } indent(); out_.putChar('"'); out_.vprintf(format, ap); out_.putChar('"'); va_end(ap); first_ = false; } void JSONPrinter::property(const char* name, int32_t value) { propertyName(name); out_.printf("%" PRId32, value); } void JSONPrinter::value(int val) { if (!first_) { out_.putChar(','); } indent(); out_.printf("%d", val); first_ = false; } void JSONPrinter::property(const char* name, uint32_t value) { propertyName(name); out_.printf("%" PRIu32, value); } void JSONPrinter::property(const char* name, int64_t value) { propertyName(name); out_.printf("%" PRId64, value); } void JSONPrinter::property(const char* name, uint64_t value) { propertyName(name); out_.printf("%" PRIu64, value); } #if defined(XP_DARWIN) || defined(__OpenBSD__) || defined(__wasi__) void JSONPrinter::property(const char* name, size_t value) { propertyName(name); out_.printf("%zu", value); } #endif void JSONPrinter::floatProperty(const char* name, double value, size_t precision) { if (!mozilla::IsFinite(value)) { propertyName(name); out_.put("null"); return; } ToCStringBuf cbuf; const char* str = NumberToCString(&cbuf, value); MOZ_ASSERT(str); property(name, str); } void JSONPrinter::property(const char* name, const mozilla::TimeDuration& dur, TimePrecision precision) { if (precision == MICROSECONDS) { property(name, static_cast(dur.ToMicroseconds())); return; } propertyName(name); lldiv_t split; switch (precision) { case SECONDS: split = lldiv(static_cast(dur.ToMilliseconds()), 1000); break; case MILLISECONDS: split = lldiv(static_cast(dur.ToMicroseconds()), 1000); break; case MICROSECONDS: MOZ_ASSERT_UNREACHABLE(""); }; out_.printf("%lld.%03lld", split.quot, split.rem); } void JSONPrinter::nullProperty(const char* name) { propertyName(name); out_.put("null"); } void JSONPrinter::nullValue() { if (!first_) { out_.putChar(','); } indent(); out_.put("null"); first_ = false; } void JSONPrinter::endObject() { indentLevel_--; indent(); out_.putChar('}'); first_ = false; } void JSONPrinter::endList() { indentLevel_--; indent(); out_.putChar(']'); first_ = false; }