Skip to content

Commit 013190d

Browse files
committed
src: use simdjson to parse SEA configuration
PR-URL: #59323 Refs: #59288 Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Daniel Lemire <[email protected]> Reviewed-By: Ethan Arrowood <[email protected]>
1 parent af20ce5 commit 013190d

File tree

2 files changed

+153
-141
lines changed

2 files changed

+153
-141
lines changed

src/node_sea.cc

Lines changed: 108 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
#include "blob_serializer_deserializer-inl.h"
44
#include "debug_utils-inl.h"
55
#include "env-inl.h"
6-
#include "json_parser.h"
76
#include "node_contextify.h"
87
#include "node_errors.h"
98
#include "node_external_reference.h"
109
#include "node_internals.h"
1110
#include "node_snapshot_builder.h"
1211
#include "node_union_bytes.h"
1312
#include "node_v8_platform-inl.h"
13+
#include "simdjson.h"
1414
#include "util-inl.h"
1515

1616
// The POSTJECT_SENTINEL_FUSE macro is a string of random characters selected by
@@ -303,79 +303,131 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
303303
}
304304

305305
SeaConfig result;
306-
JSONParser parser;
307-
if (!parser.Parse(config)) {
308-
FPrintF(stderr, "Cannot parse JSON from %s\n", config_path);
309-
return std::nullopt;
310-
}
311306

312-
result.main_path =
313-
parser.GetTopLevelStringField("main").value_or(std::string());
314-
if (result.main_path.empty()) {
315-
FPrintF(stderr,
316-
"\"main\" field of %s is not a non-empty string\n",
317-
config_path);
318-
return std::nullopt;
319-
}
307+
simdjson::ondemand::parser parser;
308+
simdjson::ondemand::document document;
309+
simdjson::ondemand::object main_object;
310+
simdjson::error_code error =
311+
parser.iterate(simdjson::pad(config)).get(document);
320312

321-
result.output_path =
322-
parser.GetTopLevelStringField("output").value_or(std::string());
323-
if (result.output_path.empty()) {
313+
if (!error) {
314+
error = document.get_object().get(main_object);
315+
}
316+
if (error) {
324317
FPrintF(stderr,
325-
"\"output\" field of %s is not a non-empty string\n",
326-
config_path);
318+
"Cannot parse JSON from %s: %s\n",
319+
config_path,
320+
simdjson::error_message(error));
327321
return std::nullopt;
328322
}
329323

330-
std::optional<bool> disable_experimental_sea_warning =
331-
parser.GetTopLevelBoolField("disableExperimentalSEAWarning");
332-
if (!disable_experimental_sea_warning.has_value()) {
333-
FPrintF(stderr,
324+
bool use_snapshot_value = false;
325+
bool use_code_cache_value = false;
326+
327+
for (auto field : main_object) {
328+
std::string_view key;
329+
if (field.unescaped_key().get(key)) {
330+
FPrintF(stderr, "Cannot read key from %s\n", config_path);
331+
return std::nullopt;
332+
}
333+
if (key == "main") {
334+
if (field.value().get_string().get(result.main_path) ||
335+
result.main_path.empty()) {
336+
FPrintF(stderr,
337+
"\"main\" field of %s is not a non-empty string\n",
338+
config_path);
339+
return std::nullopt;
340+
}
341+
} else if (key == "output") {
342+
if (field.value().get_string().get(result.output_path) ||
343+
result.output_path.empty()) {
344+
FPrintF(stderr,
345+
"\"output\" field of %s is not a non-empty string\n",
346+
config_path);
347+
return std::nullopt;
348+
}
349+
} else if (key == "disableExperimentalSEAWarning") {
350+
bool disable_experimental_sea_warning;
351+
if (field.value().get_bool().get(disable_experimental_sea_warning)) {
352+
FPrintF(
353+
stderr,
334354
"\"disableExperimentalSEAWarning\" field of %s is not a Boolean\n",
335355
config_path);
336-
return std::nullopt;
337-
}
338-
if (disable_experimental_sea_warning.value()) {
339-
result.flags |= SeaFlags::kDisableExperimentalSeaWarning;
356+
return std::nullopt;
357+
}
358+
if (disable_experimental_sea_warning) {
359+
result.flags |= SeaFlags::kDisableExperimentalSeaWarning;
360+
}
361+
} else if (key == "useSnapshot") {
362+
if (field.value().get_bool().get(use_snapshot_value)) {
363+
FPrintF(stderr,
364+
"\"useSnapshot\" field of %s is not a Boolean\n",
365+
config_path);
366+
return std::nullopt;
367+
}
368+
if (use_snapshot_value) {
369+
result.flags |= SeaFlags::kUseSnapshot;
370+
}
371+
} else if (key == "useCodeCache") {
372+
if (field.value().get_bool().get(use_code_cache_value)) {
373+
FPrintF(stderr,
374+
"\"useCodeCache\" field of %s is not a Boolean\n",
375+
config_path);
376+
return std::nullopt;
377+
}
378+
if (use_code_cache_value) {
379+
result.flags |= SeaFlags::kUseCodeCache;
380+
}
381+
} else if (key == "assets") {
382+
simdjson::ondemand::object assets_object;
383+
if (field.value().get_object().get(assets_object)) {
384+
FPrintF(stderr,
385+
"\"assets\" field of %s is not a map of strings\n",
386+
config_path);
387+
return std::nullopt;
388+
}
389+
simdjson::ondemand::value asset_value;
390+
for (auto asset_field : assets_object) {
391+
std::string_view key_str;
392+
std::string_view value_str;
393+
if (asset_field.unescaped_key().get(key_str) ||
394+
asset_field.value().get(asset_value) ||
395+
asset_value.get_string().get(value_str)) {
396+
FPrintF(stderr,
397+
"\"assets\" field of %s is not a map of strings\n",
398+
config_path);
399+
return std::nullopt;
400+
}
401+
402+
result.assets.emplace(key_str, value_str);
403+
}
404+
405+
if (!result.assets.empty()) {
406+
result.flags |= SeaFlags::kIncludeAssets;
407+
}
408+
}
340409
}
341410

342-
std::optional<bool> use_snapshot = parser.GetTopLevelBoolField("useSnapshot");
343-
if (!use_snapshot.has_value()) {
344-
FPrintF(
345-
stderr, "\"useSnapshot\" field of %s is not a Boolean\n", config_path);
346-
return std::nullopt;
347-
}
348-
if (use_snapshot.value()) {
349-
result.flags |= SeaFlags::kUseSnapshot;
411+
if (static_cast<bool>(result.flags & SeaFlags::kUseSnapshot) &&
412+
static_cast<bool>(result.flags & SeaFlags::kUseCodeCache)) {
413+
// TODO(joyeecheung): code cache in snapshot should be configured by
414+
// separate snapshot configurations.
415+
FPrintF(stderr,
416+
"\"useCodeCache\" is redundant when \"useSnapshot\" is true\n");
350417
}
351418

352-
std::optional<bool> use_code_cache =
353-
parser.GetTopLevelBoolField("useCodeCache");
354-
if (!use_code_cache.has_value()) {
355-
FPrintF(
356-
stderr, "\"useCodeCache\" field of %s is not a Boolean\n", config_path);
419+
if (result.main_path.empty()) {
420+
FPrintF(stderr,
421+
"\"main\" field of %s is not a non-empty string\n",
422+
config_path);
357423
return std::nullopt;
358424
}
359-
if (use_code_cache.value()) {
360-
if (use_snapshot.value()) {
361-
// TODO(joyeecheung): code cache in snapshot should be configured by
362-
// separate snapshot configurations.
363-
FPrintF(stderr,
364-
"\"useCodeCache\" is redundant when \"useSnapshot\" is true\n");
365-
} else {
366-
result.flags |= SeaFlags::kUseCodeCache;
367-
}
368-
}
369425

370-
auto assets_opt = parser.GetTopLevelStringDict("assets");
371-
if (!assets_opt.has_value()) {
426+
if (result.output_path.empty()) {
372427
FPrintF(stderr,
373-
"\"assets\" field of %s is not a map of strings\n",
428+
"\"output\" field of %s is not a non-empty string\n",
374429
config_path);
375430
return std::nullopt;
376-
} else if (!assets_opt.value().empty()) {
377-
result.flags |= SeaFlags::kIncludeAssets;
378-
result.assets = std::move(assets_opt.value());
379431
}
380432

381433
return result;

0 commit comments

Comments
 (0)