From 111b1c20170f3fb313f628d38189432d109c15a0 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 1 Aug 2025 02:30:08 -0700 Subject: [PATCH] src: use simdjson for json parser --- src/json_parser.cc | 215 +++++++++--------- src/json_parser.h | 13 +- ...st-single-executable-blob-config-errors.js | 4 +- 3 files changed, 117 insertions(+), 115 deletions(-) diff --git a/src/json_parser.cc b/src/json_parser.cc index 36d51e7d89d961..1297bbc6e8b5cd 100644 --- a/src/json_parser.cc +++ b/src/json_parser.cc @@ -1,156 +1,163 @@ #include "json_parser.h" -#include "node_errors.h" -#include "node_v8_platform-inl.h" -#include "util-inl.h" +#include +#include "debug_utils.h" namespace node { -using v8::Array; -using v8::Context; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::String; -using v8::Value; JSONParser::JSONParser() {} bool JSONParser::Parse(const std::string& content) { DCHECK(!parsed_); - Isolate* isolate = isolate_.get(); - v8::Locker locker(isolate); - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - - Local context = Context::New(isolate); - Context::Scope context_scope(context); - - // It's not a real script, so don't print the source line. - errors::PrinterTryCatch bootstrapCatch( - isolate, errors::PrinterTryCatch::kDontPrintSourceLine); - Local json_string_value; - Local result_value; - if (!ToV8Value(context, content).ToLocal(&json_string_value) || - !json_string_value->IsString() || - !v8::JSON::Parse(context, json_string_value.As()) - .ToLocal(&result_value) || - !result_value->IsObject()) { + json_content_ = content; + size_t json_length = json_content_.size(); + json_content_.append(simdjson::SIMDJSON_PADDING, ' '); + + simdjson::padded_string_view json_view( + json_content_.data(), json_length, json_content_.size()); + + simdjson::ondemand::document document; + simdjson::error_code error = parser_.iterate(json_view).get(document); + + if (error != simdjson::SUCCESS) { + error_message_ = simdjson::error_message(error); + std::fprintf(stderr, "%s\n", error_message_.c_str()); return false; } - context_.Reset(isolate, context); - content_.Reset(isolate, result_value.As()); - parsed_ = true; + simdjson::ondemand::object obj; + error = document.get_object().get(obj); + if (error != simdjson::SUCCESS) { + error_message_ = simdjson::error_message(error); + std::fprintf(stderr, "%s\n", error_message_.c_str()); + return false; + } + parsed_ = true; return true; } std::optional JSONParser::GetTopLevelStringField( std::string_view field) { - Isolate* isolate = isolate_.get(); - v8::Locker locker(isolate); - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - - Local context = context_.Get(isolate); - Context::Scope context_scope(context); - - Local content_object = content_.Get(isolate); - - Local value; - // It's not a real script, so don't print the source line. - errors::PrinterTryCatch bootstrapCatch( - isolate, errors::PrinterTryCatch::kDontPrintSourceLine); - Local field_local; - if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) { + if (!parsed_) { return {}; } - if (!content_object->Get(context, field_local).ToLocal(&value) || - !value->IsString()) { + + simdjson::padded_string_view json_view( + json_content_.data(), + json_content_.size() - simdjson::SIMDJSON_PADDING, + json_content_.size()); + + simdjson::ondemand::document document; + simdjson::error_code error = parser_.iterate(json_view).get(document); + if (error != simdjson::SUCCESS) { return {}; } - Utf8Value utf8_value(isolate, value); - return utf8_value.ToString(); + + simdjson::ondemand::object obj; + error = document.get_object().get(obj); + if (error != simdjson::SUCCESS) { + return {}; + } + + std::string_view result; + error = obj[field].get_string().get(result); + if (error != simdjson::SUCCESS) { + return {}; + } + + return std::string(result); } std::optional JSONParser::GetTopLevelBoolField(std::string_view field) { - Isolate* isolate = isolate_.get(); - v8::Locker locker(isolate); - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - - Local context = context_.Get(isolate); - Context::Scope context_scope(context); - - Local content_object = content_.Get(isolate); - Local value; - bool has_field; - // It's not a real script, so don't print the source line. - errors::PrinterTryCatch bootstrapCatch( - isolate, errors::PrinterTryCatch::kDontPrintSourceLine); - Local field_local; - if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) { + if (!parsed_) { return {}; } - if (!content_object->Has(context, field_local).To(&has_field)) { + + simdjson::padded_string_view json_view( + json_content_.data(), + json_content_.size() - simdjson::SIMDJSON_PADDING, + json_content_.size()); + + simdjson::ondemand::document document; + simdjson::error_code error = parser_.iterate(json_view).get(document); + if (error != simdjson::SUCCESS) { return {}; } - if (!has_field) { + + simdjson::ondemand::object obj; + error = document.get_object().get(obj); + if (error != simdjson::SUCCESS) { + return {}; + } + + simdjson::ondemand::value val; + error = obj[field].get(val); + if (error != simdjson::SUCCESS) { return false; } - if (!content_object->Get(context, field_local).ToLocal(&value) || - !value->IsBoolean()) { + + bool result; + error = val.get_bool().get(result); + if (error != simdjson::SUCCESS) { return {}; } - return value->BooleanValue(isolate); + + return result; } std::optional JSONParser::GetTopLevelStringDict( std::string_view field) { - Isolate* isolate = isolate_.get(); - v8::Locker locker(isolate); - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - Local context = context_.Get(isolate); - Local content_object = content_.Get(isolate); - Local value; - bool has_field; - // It's not a real script, so don't print the source line. - errors::PrinterTryCatch bootstrapCatch( - isolate, errors::PrinterTryCatch::kDontPrintSourceLine); - Local field_local; - if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) { + if (!parsed_) { return std::nullopt; } - if (!content_object->Has(context, field_local).To(&has_field)) { + + simdjson::padded_string_view json_view( + json_content_.data(), + json_content_.size() - simdjson::SIMDJSON_PADDING, + json_content_.size()); + + simdjson::ondemand::document document; + simdjson::error_code error = parser_.iterate(json_view).get(document); + if (error != simdjson::SUCCESS) { return std::nullopt; } - if (!has_field) { - return StringDict(); - } - if (!content_object->Get(context, field_local).ToLocal(&value) || - !value->IsObject()) { + + simdjson::ondemand::object obj; + error = document.get_object().get(obj); + if (error != simdjson::SUCCESS) { return std::nullopt; } - Local dict = value.As(); - Local keys; - if (!dict->GetOwnPropertyNames(context).ToLocal(&keys)) { + + simdjson::ondemand::value val; + error = obj[field].get(val); + if (error != simdjson::SUCCESS) { + return StringDict(); + } + + simdjson::ondemand::object dict; + error = val.get_object().get(dict); + if (error != simdjson::SUCCESS) { return std::nullopt; } - std::unordered_map result; - uint32_t length = keys->Length(); - for (uint32_t i = 0; i < length; ++i) { - Local key; - Local value; - if (!keys->Get(context, i).ToLocal(&key) || !key->IsString()) + + StringDict result; + for (auto field_value : dict) { + std::string_view key_view; + error = field_value.unescaped_key().get(key_view); + if (error != simdjson::SUCCESS) { return StringDict(); - if (!dict->Get(context, key).ToLocal(&value) || !value->IsString()) + } + + std::string_view value_view; + error = field_value.value().get_string().get(value_view); + if (error != simdjson::SUCCESS) { return StringDict(); + } - Utf8Value key_utf8(isolate, key); - Utf8Value value_utf8(isolate, value); - result.emplace(*key_utf8, *value_utf8); + result.emplace(std::string(key_view), std::string(value_view)); } + return result; } diff --git a/src/json_parser.h b/src/json_parser.h index d491736d68fb0a..e18ee223de573f 100644 --- a/src/json_parser.h +++ b/src/json_parser.h @@ -7,8 +7,7 @@ #include #include #include -#include "util.h" -#include "v8.h" +#include "simdjson.h" namespace node { // This is intended to be used to get some top-level fields out of a JSON @@ -23,14 +22,12 @@ class JSONParser { std::optional GetTopLevelStringField(std::string_view field); std::optional GetTopLevelBoolField(std::string_view field); std::optional GetTopLevelStringDict(std::string_view field); + std::string GetErrorMessage() const { return error_message_; } private: - // We might want a lighter-weight JSON parser for this use case. But for now - // using V8 is good enough. - RAIIIsolateWithoutEntering isolate_; - - v8::Global context_; - v8::Global content_; + simdjson::ondemand::parser parser_; + std::string json_content_; + std::string error_message_; bool parsed_ = false; }; } // namespace node diff --git a/test/parallel/test-single-executable-blob-config-errors.js b/test/parallel/test-single-executable-blob-config-errors.js index 364a533c0c90fb..a2319f466542af 100644 --- a/test/parallel/test-single-executable-blob-config-errors.js +++ b/test/parallel/test-single-executable-blob-config-errors.js @@ -50,9 +50,7 @@ const assert = require('assert'); ['--experimental-sea-config', config], { cwd: tmpdir.path, }); - const stderr = child.stderr.toString(); - assert.strictEqual(child.status, 1); - assert.match(stderr, /SyntaxError: Expected ':' after property name/); + const stderr = child.stderr.toString(); assert.match(stderr, /INCOMPLETE_ARRAY_OR_OBJECT/); assert( stderr.includes( `Cannot parse JSON from ${config}`