diff --git a/node.gyp b/node.gyp index bbe796bc24a9b5..256415c9b83ebe 100644 --- a/node.gyp +++ b/node.gyp @@ -560,6 +560,7 @@ 'src/js_native_api_v8.h', 'src/js_native_api_v8_internals.h', 'src/js_stream.cc', + 'src/json_utils.cc', 'src/module_wrap.cc', 'src/node.cc', 'src/node_api.cc', @@ -644,6 +645,7 @@ 'src/histogram.h', 'src/histogram-inl.h', 'src/js_stream.h', + 'src/json_utils.h', 'src/large_pages/node_large_page.cc', 'src/large_pages/node_large_page.h', 'src/memory_tracker.h', @@ -1141,7 +1143,7 @@ 'test/cctest/test_linked_binding.cc', 'test/cctest/test_per_process.cc', 'test/cctest/test_platform.cc', - 'test/cctest/test_report_util.cc', + 'test/cctest/test_json_utils.cc', 'test/cctest/test_sockaddr.cc', 'test/cctest/test_traced_value.cc', 'test/cctest/test_util.cc', diff --git a/src/json_utils.cc b/src/json_utils.cc new file mode 100644 index 00000000000000..aa03a6d7305d75 --- /dev/null +++ b/src/json_utils.cc @@ -0,0 +1,67 @@ +#include "json_utils.h" + +namespace node { + +std::string EscapeJsonChars(const std::string& str) { + const std::string control_symbols[0x20] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", + "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", + "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", + "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f" + }; + + std::string ret; + size_t last_pos = 0; + size_t pos = 0; + for (; pos < str.size(); ++pos) { + std::string replace; + char ch = str[pos]; + if (ch == '\\') { + replace = "\\\\"; + } else if (ch == '\"') { + replace = "\\\""; + } else { + size_t num = static_cast(ch); + if (num < 0x20) replace = control_symbols[num]; + } + if (!replace.empty()) { + if (pos > last_pos) { + ret += str.substr(last_pos, pos - last_pos); + } + last_pos = pos + 1; + ret += replace; + } + } + // Append any remaining symbols. + if (last_pos < str.size()) { + ret += str.substr(last_pos, pos - last_pos); + } + return ret; +} + +std::string Reindent(const std::string& str, int indent_depth) { + std::string indent; + for (int i = 0; i < indent_depth; i++) indent += ' '; + + std::string out; + std::string::size_type pos = 0; + do { + std::string::size_type prev_pos = pos; + pos = str.find('\n', pos); + + out.append(indent); + + if (pos == std::string::npos) { + out.append(str, prev_pos, std::string::npos); + break; + } else { + pos++; + out.append(str, prev_pos, pos - prev_pos); + } + } while (true); + + return out; +} + +} // namespace node diff --git a/src/json_utils.h b/src/json_utils.h new file mode 100644 index 00000000000000..21e204328058ee --- /dev/null +++ b/src/json_utils.h @@ -0,0 +1,160 @@ +#ifndef SRC_JSON_UTILS_H_ +#define SRC_JSON_UTILS_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include + +namespace node { + +std::string EscapeJsonChars(const std::string& str); +std::string Reindent(const std::string& str, int indentation); + +// JSON compiler definitions. +class JSONWriter { + public: + JSONWriter(std::ostream& out, bool compact) + : out_(out), compact_(compact) {} + + private: + inline void indent() { indent_ += 2; } + inline void deindent() { indent_ -= 2; } + inline void advance() { + if (compact_) return; + for (int i = 0; i < indent_; i++) out_ << ' '; + } + inline void write_one_space() { + if (compact_) return; + out_ << ' '; + } + inline void write_new_line() { + if (compact_) return; + out_ << '\n'; + } + + public: + inline void json_start() { + if (state_ == kAfterValue) out_ << ','; + write_new_line(); + advance(); + out_ << '{'; + indent(); + state_ = kObjectStart; + } + + inline void json_end() { + write_new_line(); + deindent(); + advance(); + out_ << '}'; + state_ = kAfterValue; + } + template + inline void json_objectstart(T key) { + if (state_ == kAfterValue) out_ << ','; + write_new_line(); + advance(); + write_string(key); + out_ << ':'; + write_one_space(); + out_ << '{'; + indent(); + state_ = kObjectStart; + } + + template + inline void json_arraystart(T key) { + if (state_ == kAfterValue) out_ << ','; + write_new_line(); + advance(); + write_string(key); + out_ << ':'; + write_one_space(); + out_ << '['; + indent(); + state_ = kObjectStart; + } + inline void json_objectend() { + write_new_line(); + deindent(); + advance(); + out_ << '}'; + if (indent_ == 0) { + // Top-level object is complete, so end the line. + out_ << '\n'; + } + state_ = kAfterValue; + } + + inline void json_arrayend() { + write_new_line(); + deindent(); + advance(); + out_ << ']'; + state_ = kAfterValue; + } + template + inline void json_keyvalue(const T& key, const U& value) { + if (state_ == kAfterValue) out_ << ','; + write_new_line(); + advance(); + write_string(key); + out_ << ':'; + write_one_space(); + write_value(value); + state_ = kAfterValue; + } + + template + inline void json_element(const U& value) { + if (state_ == kAfterValue) out_ << ','; + write_new_line(); + advance(); + write_value(value); + state_ = kAfterValue; + } + + struct Null {}; // Usable as a JSON value. + + struct ForeignJSON { + std::string as_string; + }; + + private: + template ::is_specialized, bool>::type> + inline void write_value(T number) { + if (std::is_same::value) + out_ << (number ? "true" : "false"); + else + out_ << number; + } + + inline void write_value(Null null) { out_ << "null"; } + inline void write_value(const char* str) { write_string(str); } + inline void write_value(const std::string& str) { write_string(str); } + + inline void write_value(const ForeignJSON& json) { + out_ << Reindent(json.as_string, indent_); + } + + inline void write_string(const std::string& str) { + out_ << '"' << EscapeJsonChars(str) << '"'; + } + inline void write_string(const char* str) { write_string(std::string(str)); } + + enum JSONState { kObjectStart, kAfterValue }; + std::ostream& out_; + bool compact_; + int indent_ = 0; + int state_ = kObjectStart; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_JSON_UTILS_H_ diff --git a/src/node_report.cc b/src/node_report.cc index ff2206dbb44ef0..98da24c9567a28 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -1,4 +1,5 @@ #include "env-inl.h" +#include "json_utils.h" #include "node_report.h" #include "debug_utils-inl.h" #include "diagnosticfilename-inl.h" @@ -16,6 +17,7 @@ #include #endif +#include #include #include #include @@ -30,6 +32,7 @@ using node::arraysize; using node::ConditionVariable; using node::DiagnosticFilename; using node::Environment; +using node::JSONWriter; using node::Mutex; using node::NativeSymbolDebuggingContext; using node::TIME_TYPE; diff --git a/src/node_report.h b/src/node_report.h index 808e302223e5f8..98490784dd6e70 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -3,7 +3,6 @@ #include "node.h" #include "node_buffer.h" #include "uv.h" -#include "v8.h" #include "util.h" #ifndef _WIN32 @@ -11,19 +10,7 @@ #include #endif -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include namespace report { @@ -43,8 +30,6 @@ void GetNodeReport(v8::Isolate* isolate, // Function declarations - utility functions in src/node_report_utils.cc void WalkHandle(uv_handle_t* h, void* arg); -std::string EscapeJsonChars(const std::string& str); -std::string Reindent(const std::string& str, int indentation); template std::string ValueToHexString(T value) { @@ -59,148 +44,4 @@ std::string ValueToHexString(T value) { void WriteReport(const v8::FunctionCallbackInfo& info); void GetReport(const v8::FunctionCallbackInfo& info); -// Node.js boot time - defined in src/node.cc -extern double prog_start_time; - -// JSON compiler definitions. -class JSONWriter { - public: - JSONWriter(std::ostream& out, bool compact) - : out_(out), compact_(compact) {} - - private: - inline void indent() { indent_ += 2; } - inline void deindent() { indent_ -= 2; } - inline void advance() { - if (compact_) return; - for (int i = 0; i < indent_; i++) out_ << ' '; - } - inline void write_one_space() { - if (compact_) return; - out_ << ' '; - } - inline void write_new_line() { - if (compact_) return; - out_ << '\n'; - } - - public: - inline void json_start() { - if (state_ == kAfterValue) out_ << ','; - write_new_line(); - advance(); - out_ << '{'; - indent(); - state_ = kObjectStart; - } - - inline void json_end() { - write_new_line(); - deindent(); - advance(); - out_ << '}'; - state_ = kAfterValue; - } - template - inline void json_objectstart(T key) { - if (state_ == kAfterValue) out_ << ','; - write_new_line(); - advance(); - write_string(key); - out_ << ':'; - write_one_space(); - out_ << '{'; - indent(); - state_ = kObjectStart; - } - - template - inline void json_arraystart(T key) { - if (state_ == kAfterValue) out_ << ','; - write_new_line(); - advance(); - write_string(key); - out_ << ':'; - write_one_space(); - out_ << '['; - indent(); - state_ = kObjectStart; - } - inline void json_objectend() { - write_new_line(); - deindent(); - advance(); - out_ << '}'; - if (indent_ == 0) { - // Top-level object is complete, so end the line. - out_ << '\n'; - } - state_ = kAfterValue; - } - - inline void json_arrayend() { - write_new_line(); - deindent(); - advance(); - out_ << ']'; - state_ = kAfterValue; - } - template - inline void json_keyvalue(const T& key, const U& value) { - if (state_ == kAfterValue) out_ << ','; - write_new_line(); - advance(); - write_string(key); - out_ << ':'; - write_one_space(); - write_value(value); - state_ = kAfterValue; - } - - template - inline void json_element(const U& value) { - if (state_ == kAfterValue) out_ << ','; - write_new_line(); - advance(); - write_value(value); - state_ = kAfterValue; - } - - struct Null {}; // Usable as a JSON value. - - struct ForeignJSON { - std::string as_string; - }; - - private: - template ::is_specialized, bool>::type> - inline void write_value(T number) { - if (std::is_same::value) - out_ << (number ? "true" : "false"); - else - out_ << number; - } - - inline void write_value(Null null) { out_ << "null"; } - inline void write_value(const char* str) { write_string(str); } - inline void write_value(const std::string& str) { write_string(str); } - - inline void write_value(const ForeignJSON& json) { - out_ << Reindent(json.as_string, indent_); - } - - inline void write_string(const std::string& str) { - out_ << '"' << EscapeJsonChars(str) << '"'; - } - inline void write_string(const char* str) { write_string(std::string(str)); } - - enum JSONState { kObjectStart, kAfterValue }; - std::ostream& out_; - bool compact_; - int indent_ = 0; - int state_ = kObjectStart; -}; - } // namespace report diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc index efa15790b9bc96..abbf6d529d0ef7 100644 --- a/src/node_report_utils.cc +++ b/src/node_report_utils.cc @@ -1,9 +1,11 @@ +#include "json_utils.h" #include "node_internals.h" #include "node_report.h" #include "util-inl.h" namespace report { +using node::JSONWriter; using node::MallocedBuffer; static constexpr auto null = JSONWriter::Null{}; @@ -225,66 +227,4 @@ void WalkHandle(uv_handle_t* h, void* arg) { writer->json_end(); } -std::string EscapeJsonChars(const std::string& str) { - const std::string control_symbols[0x20] = { - "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", - "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", - "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", - "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", - "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f" - }; - - std::string ret; - size_t last_pos = 0; - size_t pos = 0; - for (; pos < str.size(); ++pos) { - std::string replace; - char ch = str[pos]; - if (ch == '\\') { - replace = "\\\\"; - } else if (ch == '\"') { - replace = "\\\""; - } else { - size_t num = static_cast(ch); - if (num < 0x20) replace = control_symbols[num]; - } - if (!replace.empty()) { - if (pos > last_pos) { - ret += str.substr(last_pos, pos - last_pos); - } - last_pos = pos + 1; - ret += replace; - } - } - // Append any remaining symbols. - if (last_pos < str.size()) { - ret += str.substr(last_pos, pos - last_pos); - } - return ret; -} - -std::string Reindent(const std::string& str, int indent_depth) { - std::string indent; - for (int i = 0; i < indent_depth; i++) indent += ' '; - - std::string out; - std::string::size_type pos = 0; - do { - std::string::size_type prev_pos = pos; - pos = str.find('\n', pos); - - out.append(indent); - - if (pos == std::string::npos) { - out.append(str, prev_pos, std::string::npos); - break; - } else { - pos++; - out.append(str, prev_pos, pos - prev_pos); - } - } while (true); - - return out; -} - } // namespace report diff --git a/test/cctest/test_report_util.cc b/test/cctest/test_json_utils.cc similarity index 90% rename from test/cctest/test_report_util.cc rename to test/cctest/test_json_utils.cc index e32558ef75b5e8..06a9074a61baf1 100644 --- a/test/cctest/test_report_util.cc +++ b/test/cctest/test_json_utils.cc @@ -1,9 +1,9 @@ -#include "node_report.h" +#include "json_utils.h" #include "gtest/gtest.h" -TEST(ReportUtilTest, EscapeJsonChars) { - using report::EscapeJsonChars; +TEST(JSONUtilsTest, EscapeJsonChars) { + using node::EscapeJsonChars; EXPECT_EQ("abc", EscapeJsonChars("abc")); EXPECT_EQ("abc\\n", EscapeJsonChars("abc\n")); EXPECT_EQ("abc\\nabc", EscapeJsonChars("abc\nabc"));