Skip to content

Commit f14e5f0

Browse files
committed
src: replace snapshot flag with affects_annotation
1 parent 9c4ca0a commit f14e5f0

8 files changed

Lines changed: 355 additions & 59 deletions

src/node.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,12 +641,14 @@ enum Flags : uint64_t {
641641
};
642642
} // namespace EnvironmentFlags
643643

644+
// Bit 0 is a config-level flag (kWithoutCodeCache).
645+
// Bits 1..31 are dynamically assigned at runtime to env-affecting boolean
646+
// options that carry affects_snapshot=true, in alphabetical order by flag
647+
// name. The mapping is computed by GetSnapshotAffectingOptions() so there are
648+
// no named constants for those bits here.
644649
enum class SnapshotFlags : uint32_t {
645650
kDefault = 0,
646651
// Whether code cache should be generated as part of the snapshot.
647-
// Code cache reduces the time spent on compiling functions included
648-
// in the snapshot at the expense of a bigger snapshot size and
649-
// potentially breaking portability of the snapshot.
650652
kWithoutCodeCache = 1 << 0,
651653
};
652654

src/node_options-inl.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ void OptionsParser<Options>::AddOption(const char* name,
3434
bool Options::*field,
3535
OptionEnvvarSettings env_setting,
3636
bool default_is_true,
37-
OptionNamespaces namespace_id) {
37+
OptionNamespaces namespace_id,
38+
bool affects_snapshot) {
3839
options_.emplace(name,
3940
OptionInfo{kBoolean,
4041
std::make_shared<SimpleOptionField<bool>>(field),
4142
env_setting,
4243
help_text,
4344
default_is_true,
44-
NamespaceEnumToString(namespace_id)});
45+
NamespaceEnumToString(namespace_id),
46+
affects_snapshot});
4547
}
4648

4749
template <typename Options>
@@ -229,7 +231,8 @@ auto OptionsParser<Options>::Convert(
229231
original.env_setting,
230232
original.help_text,
231233
original.default_is_true,
232-
original.namespace_id};
234+
original.namespace_id,
235+
original.affects_snapshot};
233236
}
234237

235238
template <typename Options>

src/node_options.cc

Lines changed: 103 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <string_view>
2323
#include <vector>
2424

25+
using v8::Array;
2526
using v8::Boolean;
2627
using v8::Context;
2728
using v8::FunctionCallbackInfo;
@@ -586,39 +587,55 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
586587
"experimental EventSource API",
587588
&EnvironmentOptions::experimental_eventsource,
588589
kAllowedInEnvvar,
589-
false);
590+
false,
591+
OptionNamespaces::kNoNamespace,
592+
true);
590593
AddOption("--experimental-fetch", "", NoOp{}, kAllowedInEnvvar);
591594
#if HAVE_FFI
592595
AddOption("--experimental-ffi",
593596
"experimental node:ffi module",
594597
&EnvironmentOptions::experimental_ffi,
595598
kAllowedInEnvvar,
596-
false);
599+
false,
600+
OptionNamespaces::kNoNamespace,
601+
true);
597602
#endif // HAVE_FFI
598603
AddOption("--experimental-websocket",
599604
"experimental WebSocket API",
600605
&EnvironmentOptions::experimental_websocket,
601606
kAllowedInEnvvar,
607+
true,
608+
OptionNamespaces::kNoNamespace,
602609
true);
603610
AddOption("--experimental-global-customevent", "", NoOp{}, kAllowedInEnvvar);
604611
AddOption("--experimental-sqlite",
605612
"experimental node:sqlite module",
606613
&EnvironmentOptions::experimental_sqlite,
607614
kAllowedInEnvvar,
608-
HAVE_SQLITE);
615+
HAVE_SQLITE,
616+
OptionNamespaces::kNoNamespace,
617+
true);
609618
AddOption("--experimental-stream-iter",
610619
"experimental iterable streams API (node:stream/iter)",
611620
&EnvironmentOptions::experimental_stream_iter,
612-
kAllowedInEnvvar);
613-
AddOption("--experimental-quic",
621+
kAllowedInEnvvar,
622+
false,
623+
OptionNamespaces::kNoNamespace,
624+
true);
614625
#ifndef OPENSSL_NO_QUIC
626+
AddOption("--experimental-quic",
615627
"experimental QUIC support",
616628
&EnvironmentOptions::experimental_quic,
629+
kAllowedInEnvvar,
630+
false,
631+
OptionNamespaces::kNoNamespace,
632+
true);
617633
#else
634+
AddOption("--experimental-quic",
618635
"" /* undocumented when no-op */,
619636
NoOp{},
620-
#endif
621637
kAllowedInEnvvar);
638+
#endif
622639
AddOption("--experimental-webstorage",
623640
"experimental Web Storage API",
624641
&EnvironmentOptions::webstorage,
@@ -720,7 +737,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
720737
AddOption("--experimental-vm-modules",
721738
"experimental ES Module support in vm module",
722739
&EnvironmentOptions::experimental_vm_modules,
723-
kAllowedInEnvvar);
740+
kAllowedInEnvvar,
741+
false,
742+
OptionNamespaces::kNoNamespace,
743+
true);
724744
AddOption("--experimental-worker", "", NoOp{}, kAllowedInEnvvar);
725745
AddOption("--experimental-report", "", NoOp{}, kAllowedInEnvvar);
726746
AddOption(
@@ -731,11 +751,20 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
731751
&EnvironmentOptions::async_context_frame,
732752
kAllowedInEnvvar,
733753
true);
734-
AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals);
754+
AddOption("--expose-internals",
755+
"",
756+
&EnvironmentOptions::expose_internals,
757+
kDisallowedInEnvvar,
758+
false,
759+
OptionNamespaces::kNoNamespace,
760+
true);
735761
AddOption("--frozen-intrinsics",
736762
"experimental frozen intrinsics support",
737763
&EnvironmentOptions::frozen_intrinsics,
738-
kAllowedInEnvvar);
764+
kAllowedInEnvvar,
765+
false,
766+
OptionNamespaces::kNoNamespace,
767+
true);
739768
AddOption("--heapsnapshot-signal",
740769
"Generate heap snapshot on specified signal",
741770
&EnvironmentOptions::heap_snapshot_signal,
@@ -763,6 +792,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
763792
"silence deprecation warnings",
764793
&EnvironmentOptions::deprecation,
765794
kAllowedInEnvvar,
795+
true,
796+
OptionNamespaces::kNoNamespace,
766797
true);
767798
AddOption("--force-async-hooks-checks",
768799
"disable checks for async_hooks",
@@ -789,6 +820,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
789820
"silence all process warnings",
790821
&EnvironmentOptions::warnings,
791822
kAllowedInEnvvar,
823+
true,
824+
OptionNamespaces::kNoNamespace,
792825
true);
793826
AddOption("--disable-warning",
794827
"silence specific process warnings",
@@ -801,7 +834,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
801834
AddOption("--pending-deprecation",
802835
"emit pending deprecation warnings",
803836
&EnvironmentOptions::pending_deprecation,
804-
kAllowedInEnvvar);
837+
kAllowedInEnvvar,
838+
false,
839+
OptionNamespaces::kNoNamespace,
840+
true);
805841
AddOption("--use-env-proxy",
806842
"parse proxy settings from HTTP_PROXY/HTTPS_PROXY/NO_PROXY"
807843
"environment variables and apply the setting in global HTTP/HTTPS "
@@ -1043,11 +1079,17 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
10431079
AddOption("--throw-deprecation",
10441080
"throw an exception on deprecations",
10451081
&EnvironmentOptions::throw_deprecation,
1046-
kAllowedInEnvvar);
1082+
kAllowedInEnvvar,
1083+
false,
1084+
OptionNamespaces::kNoNamespace,
1085+
true);
10471086
AddOption("--trace-deprecation",
10481087
"show stack traces on deprecations",
10491088
&EnvironmentOptions::trace_deprecation,
1050-
kAllowedInEnvvar);
1089+
kAllowedInEnvvar,
1090+
false,
1091+
OptionNamespaces::kNoNamespace,
1092+
true);
10511093
AddOption("--trace-exit",
10521094
"show stack trace when an environment exits",
10531095
&EnvironmentOptions::trace_exit,
@@ -1068,7 +1110,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
10681110
AddOption("--trace-warnings",
10691111
"show stack traces on process warnings",
10701112
&EnvironmentOptions::trace_warnings,
1071-
kAllowedInEnvvar);
1113+
kAllowedInEnvvar,
1114+
false,
1115+
OptionNamespaces::kNoNamespace,
1116+
true);
10721117
AddOption("--trace-promises",
10731118
"show stack traces on promise initialization and resolution",
10741119
&EnvironmentOptions::trace_promises,
@@ -2017,6 +2062,34 @@ void GetNamespaceOptionsInputType(const FunctionCallbackInfo<Value>& args) {
20172062
args.GetReturnValue().Set(namespaces_metadata);
20182063
}
20192064

2065+
// Return an array of {name, defaultIsTrue} for every boolean option marked
2066+
// affects_snapshot=true, sorted alphabetically.
2067+
void GetSnapshotAffectingFlags(const FunctionCallbackInfo<Value>& args) {
2068+
Isolate* isolate = args.GetIsolate();
2069+
Local<Context> context = isolate->GetCurrentContext();
2070+
Environment* env = Environment::GetCurrent(context);
2071+
2072+
Mutex::ScopedLock lock(per_process::cli_options_mutex);
2073+
2074+
auto options = node::GetSnapshotAffectingOptions(*env->options());
2075+
2076+
Local<Array> result = Array::New(isolate, options.size());
2077+
for (size_t i = 0; i < options.size(); i++) {
2078+
Local<Object> obj = Object::New(isolate);
2079+
obj->Set(context,
2080+
FIXED_ONE_BYTE_STRING(isolate, "name"),
2081+
ToV8Value(context, options[i].name).ToLocalChecked())
2082+
.Check();
2083+
obj->Set(context,
2084+
FIXED_ONE_BYTE_STRING(isolate, "defaultIsTrue"),
2085+
Boolean::New(isolate, options[i].default_is_true))
2086+
.Check();
2087+
result->Set(context, i, obj).Check();
2088+
}
2089+
2090+
args.GetReturnValue().Set(result);
2091+
}
2092+
20202093
// Return an array containing all currently active options as flag
20212094
// strings from all sources (command line, NODE_OPTIONS, config file)
20222095
void GetOptionsAsFlags(const FunctionCallbackInfo<Value>& args) {
@@ -2138,6 +2211,8 @@ void Initialize(Local<Object> target,
21382211
context, target, "getCLIOptionsValues", GetCLIOptionsValues);
21392212
SetMethodNoSideEffect(
21402213
context, target, "getCLIOptionsInfo", GetCLIOptionsInfo);
2214+
SetMethodNoSideEffect(
2215+
context, target, "getSnapshotAffectingFlags", GetSnapshotAffectingFlags);
21412216
SetMethodNoSideEffect(
21422217
context, target, "getOptionsAsFlags", GetOptionsAsFlags);
21432218
SetMethodNoSideEffect(
@@ -2172,6 +2247,7 @@ void Initialize(Local<Object> target,
21722247
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
21732248
registry->Register(GetCLIOptionsValues);
21742249
registry->Register(GetCLIOptionsInfo);
2250+
registry->Register(GetSnapshotAffectingFlags);
21752251
registry->Register(GetOptionsAsFlags);
21762252
registry->Register(GetEmbedderOptions);
21772253
registry->Register(GetEnvOptionsInputType);
@@ -2246,6 +2322,20 @@ std::vector<std::string> ParseNodeOptionsEnvVar(
22462322
}
22472323
return env_argv;
22482324
}
2325+
std::vector<SnapshotFlagInfo> GetSnapshotAffectingOptions(
2326+
const EnvironmentOptions& opts) {
2327+
std::vector<SnapshotFlagInfo> result;
2328+
options_parser::_eop_instance.ForEachSnapshotAffecting(
2329+
const_cast<EnvironmentOptions*>(&opts),
2330+
[&](const std::string& name, bool val, bool default_is_true) {
2331+
result.push_back({name, val, default_is_true});
2332+
});
2333+
std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
2334+
return a.name < b.name;
2335+
});
2336+
return result;
2337+
}
2338+
22492339
} // namespace node
22502340

22512341
NODE_BINDING_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize)

src/node_options.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,13 +476,16 @@ class OptionsParser {
476476
// default_is_true is only a hint in printing help text, it does not
477477
// affect the default value of the option. Set the default value in the
478478
// Options struct instead.
479+
// affects_snapshot marks that the flag's effective value must match between
480+
// snapshot build and load time.
479481
void AddOption(
480482
const char* name,
481483
const char* help_text,
482484
bool Options::*field,
483485
OptionEnvvarSettings env_setting = kDisallowedInEnvvar,
484486
bool default_is_true = false,
485-
OptionNamespaces namespace_id = OptionNamespaces::kNoNamespace);
487+
OptionNamespaces namespace_id = OptionNamespaces::kNoNamespace,
488+
bool affects_snapshot = false);
486489
void AddOption(
487490
const char* name,
488491
const char* help_text,
@@ -573,6 +576,18 @@ class OptionsParser {
573576
OptionEnvvarSettings required_env_settings,
574577
std::vector<std::string>* const errors) const;
575578

579+
// Calls fn(name, value, default_is_true) for every boolean option that has
580+
// affects_snapshot=true, in unspecified order.
581+
template <typename Fn>
582+
void ForEachSnapshotAffecting(Options* options, Fn&& fn) const {
583+
for (const auto& [name, info] : options_) {
584+
if (info.affects_snapshot && info.type == kBoolean && info.field) {
585+
bool val = *Lookup<bool>(info.field, options);
586+
fn(name, val, info.default_is_true);
587+
}
588+
}
589+
}
590+
576591
private:
577592
// We support the wide variety of different option types by remembering
578593
// how to access them, given a certain `Options` struct.
@@ -622,6 +637,7 @@ class OptionsParser {
622637
std::string help_text;
623638
bool default_is_true = false;
624639
std::string namespace_id;
640+
bool affects_snapshot = false;
625641
};
626642

627643
// An implied option is composed of the information on where to store a
@@ -693,6 +709,20 @@ void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,
693709

694710
std::vector<std::string> ParseNodeOptionsEnvVar(
695711
const std::string& node_options, std::vector<std::string>* errors);
712+
713+
// Describes one snapshot-affecting boolean option.
714+
struct SnapshotFlagInfo {
715+
std::string name; // canonical CLI flag name
716+
bool value; // current effective value
717+
bool default_is_true; // true if the flag defaults to on (--no-* form)
718+
};
719+
720+
// Returns every boolean EnvironmentOption marked affects_snapshot=true,
721+
// sorted alphabetically by name. The sort order determines bit positions
722+
// in SnapshotFlags (bit 1 = index 0, bit 2 = index 1, ...).
723+
std::vector<SnapshotFlagInfo> GetSnapshotAffectingOptions(
724+
const EnvironmentOptions& opts);
725+
696726
} // namespace node
697727

698728
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

0 commit comments

Comments
 (0)