10
10
#include " debug_utils-inl.h"
11
11
#include " encoding_binding.h"
12
12
#include " env-inl.h"
13
+ #include " json_parser.h"
13
14
#include " node_blob.h"
14
15
#include " node_builtins.h"
15
16
#include " node_contextify.h"
@@ -541,6 +542,7 @@ SnapshotMetadata SnapshotDeserializer::Read() {
541
542
result.node_arch = ReadString ();
542
543
result.node_platform = ReadString ();
543
544
result.v8_cache_version_tag = ReadArithmetic<uint32_t >();
545
+ result.flags = static_cast <SnapshotFlags>(ReadArithmetic<uint32_t >());
544
546
545
547
if (is_debug) {
546
548
std::string str = ToStr (result);
@@ -570,6 +572,9 @@ size_t SnapshotSerializer::Write(const SnapshotMetadata& data) {
570
572
Debug (" Write V8 cached data version tag %" PRIx32 " \n " ,
571
573
data.v8_cache_version_tag );
572
574
written_total += WriteArithmetic<uint32_t >(data.v8_cache_version_tag );
575
+ Debug (" Write snapshot flags %" PRIx32 " \n " ,
576
+ static_cast <uint32_t >(data.flags ));
577
+ written_total += WriteArithmetic<uint32_t >(static_cast <uint32_t >(data.flags ));
573
578
return written_total;
574
579
}
575
580
@@ -690,19 +695,21 @@ bool SnapshotData::Check() const {
690
695
return false ;
691
696
}
692
697
693
- uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag ();
694
- if (metadata.v8_cache_version_tag != current_cache_version &&
695
- metadata.type == SnapshotMetadata::Type::kFullyCustomized ) {
696
- // For now we only do this check for the customized snapshots - we know
697
- // that the flags we use in the default snapshot are limited and safe
698
- // enough so we can relax the constraints for it.
699
- fprintf (stderr,
700
- " Failed to load the startup snapshot because it was built with "
701
- " a different version of V8 or with different V8 configurations.\n "
702
- " Expected tag %" PRIx32 " , read %" PRIx32 " \n " ,
703
- current_cache_version,
704
- metadata.v8_cache_version_tag );
705
- return false ;
698
+ if (metadata.type == SnapshotMetadata::Type::kFullyCustomized &&
699
+ !WithoutCodeCache (metadata.flags )) {
700
+ uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag ();
701
+ if (metadata.v8_cache_version_tag != current_cache_version) {
702
+ // For now we only do this check for the customized snapshots - we know
703
+ // that the flags we use in the default snapshot are limited and safe
704
+ // enough so we can relax the constraints for it.
705
+ fprintf (stderr,
706
+ " Failed to load the startup snapshot because it was built with "
707
+ " a different version of V8 or with different V8 configurations.\n "
708
+ " Expected tag %" PRIx32 " , read %" PRIx32 " \n " ,
709
+ current_cache_version,
710
+ metadata.v8_cache_version_tag );
711
+ return false ;
712
+ }
706
713
}
707
714
708
715
// TODO(joyeecheung): check incompatible Node.js flags.
@@ -912,23 +919,91 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
912
919
const_cast <v8::StartupData*>(&(data->v8_snapshot_blob_data ));
913
920
}
914
921
922
+ SnapshotFlags operator |(SnapshotFlags x, SnapshotFlags y) {
923
+ return static_cast <SnapshotFlags>(static_cast <uint32_t >(x) |
924
+ static_cast <uint32_t >(y));
925
+ }
926
+
927
+ SnapshotFlags operator &(SnapshotFlags x, SnapshotFlags y) {
928
+ return static_cast <SnapshotFlags>(static_cast <uint32_t >(x) &
929
+ static_cast <uint32_t >(y));
930
+ }
931
+
932
+ SnapshotFlags operator |=(/* NOLINT (runtime/references) */ SnapshotFlags& x,
933
+ SnapshotFlags y) {
934
+ return x = x | y;
935
+ }
936
+
937
+ bool WithoutCodeCache (const SnapshotFlags& flags) {
938
+ return static_cast <bool >(flags & SnapshotFlags::kWithoutCodeCache );
939
+ }
940
+
941
+ bool WithoutCodeCache (const SnapshotConfig& config) {
942
+ return WithoutCodeCache (config.flags );
943
+ }
944
+
945
+ std::optional<SnapshotConfig> ReadSnapshotConfig (const char * config_path) {
946
+ std::string config_content;
947
+ int r = ReadFileSync (&config_content, config_path);
948
+ if (r != 0 ) {
949
+ FPrintF (stderr,
950
+ " Cannot read snapshot configuration from %s: %s\n " ,
951
+ config_path,
952
+ uv_strerror (r));
953
+ return std::nullopt;
954
+ }
955
+
956
+ JSONParser parser;
957
+ if (!parser.Parse (config_content)) {
958
+ FPrintF (stderr, " Cannot parse JSON from %s\n " , config_path);
959
+ return std::nullopt;
960
+ }
961
+
962
+ SnapshotConfig result;
963
+ result.builder_script_path = parser.GetTopLevelStringField (" builder" );
964
+ if (!result.builder_script_path .has_value ()) {
965
+ FPrintF (stderr,
966
+ " \" builder\" field of %s is not a non-empty string\n " ,
967
+ config_path);
968
+ return std::nullopt;
969
+ }
970
+
971
+ std::optional<bool > WithoutCodeCache =
972
+ parser.GetTopLevelBoolField (" withoutCodeCache" );
973
+ if (!WithoutCodeCache.has_value ()) {
974
+ FPrintF (stderr,
975
+ " \" withoutCodeCache\" field of %s is not a boolean\n " ,
976
+ config_path);
977
+ return std::nullopt;
978
+ }
979
+ if (WithoutCodeCache.value ()) {
980
+ result.flags |= SnapshotFlags::kWithoutCodeCache ;
981
+ }
982
+
983
+ return result;
984
+ }
985
+
915
986
ExitCode BuildSnapshotWithoutCodeCache (
916
987
SnapshotData* out,
917
988
const std::vector<std::string>& args,
918
989
const std::vector<std::string>& exec_args,
919
- std::optional<std::string_view> main_script) {
990
+ std::optional<std::string_view> builder_script_content,
991
+ const SnapshotConfig& config) {
992
+ DCHECK (builder_script_content.has_value () ==
993
+ config.builder_script_path .has_value ());
920
994
// The default snapshot is meant to be runtime-independent and has more
921
995
// restrictions. We do not enable the inspector and do not run the event
922
996
// loop when building the default snapshot to avoid inconsistencies, but
923
997
// we do for the fully customized one, and they are expected to fixup the
924
998
// inconsistencies using v8.startupSnapshot callbacks.
925
999
SnapshotMetadata::Type snapshot_type =
926
- main_script.has_value () ? SnapshotMetadata::Type::kFullyCustomized
927
- : SnapshotMetadata::Type::kDefault ;
1000
+ builder_script_content.has_value ()
1001
+ ? SnapshotMetadata::Type::kFullyCustomized
1002
+ : SnapshotMetadata::Type::kDefault ;
928
1003
929
1004
std::vector<std::string> errors;
930
1005
auto setup = CommonEnvironmentSetup::CreateForSnapshotting (
931
- per_process::v8_platform.Platform (), &errors, args, exec_args);
1006
+ per_process::v8_platform.Platform (), &errors, args, exec_args, config );
932
1007
if (!setup) {
933
1008
for (const std::string& err : errors)
934
1009
fprintf (stderr, " %s: %s\n " , args[0 ].c_str (), err.c_str ());
@@ -954,7 +1029,7 @@ ExitCode BuildSnapshotWithoutCodeCache(
954
1029
#if HAVE_INSPECTOR
955
1030
env->InitializeInspector ({});
956
1031
#endif
957
- if (LoadEnvironment (env, main_script .value ()).IsEmpty ()) {
1032
+ if (LoadEnvironment (env, builder_script_content .value ()).IsEmpty ()) {
958
1033
return ExitCode::kGenericUserError ;
959
1034
}
960
1035
@@ -969,8 +1044,7 @@ ExitCode BuildSnapshotWithoutCodeCache(
969
1044
}
970
1045
}
971
1046
972
- return SnapshotBuilder::CreateSnapshot (
973
- out, setup.get (), static_cast <uint8_t >(snapshot_type));
1047
+ return SnapshotBuilder::CreateSnapshot (out, setup.get ());
974
1048
}
975
1049
976
1050
ExitCode BuildCodeCacheFromSnapshot (SnapshotData* out,
@@ -1014,28 +1088,32 @@ ExitCode SnapshotBuilder::Generate(
1014
1088
SnapshotData* out,
1015
1089
const std::vector<std::string>& args,
1016
1090
const std::vector<std::string>& exec_args,
1017
- std::optional<std::string_view> main_script) {
1018
- ExitCode code =
1019
- BuildSnapshotWithoutCodeCache (out, args, exec_args, main_script);
1091
+ std::optional<std::string_view> builder_script_content,
1092
+ const SnapshotConfig& snapshot_config) {
1093
+ ExitCode code = BuildSnapshotWithoutCodeCache (
1094
+ out, args, exec_args, builder_script_content, snapshot_config);
1020
1095
if (code != ExitCode::kNoFailure ) {
1021
1096
return code;
1022
1097
}
1023
1098
1024
- #ifdef NODE_USE_NODE_CODE_CACHE
1025
- // Deserialize the snapshot to recompile code cache. We need to do this in the
1026
- // second pass because V8 requires the code cache to be compiled with a
1027
- // finalized read-only space.
1028
- return BuildCodeCacheFromSnapshot (out, args, exec_args);
1029
- #else
1099
+ if (!WithoutCodeCache (snapshot_config)) {
1100
+ // Deserialize the snapshot to recompile code cache. We need to do this in
1101
+ // the second pass because V8 requires the code cache to be compiled with a
1102
+ // finalized read-only space.
1103
+ return BuildCodeCacheFromSnapshot (out, args, exec_args);
1104
+ }
1105
+
1030
1106
return ExitCode::kNoFailure ;
1031
- #endif
1032
1107
}
1033
1108
1034
1109
ExitCode SnapshotBuilder::CreateSnapshot (SnapshotData* out,
1035
- CommonEnvironmentSetup* setup,
1036
- uint8_t snapshot_type_u8) {
1110
+ CommonEnvironmentSetup* setup) {
1111
+ const SnapshotConfig* config = setup->isolate_data ()->snapshot_config ();
1112
+ DCHECK_NOT_NULL (config);
1037
1113
SnapshotMetadata::Type snapshot_type =
1038
- static_cast <SnapshotMetadata::Type>(snapshot_type_u8);
1114
+ config->builder_script_path .has_value ()
1115
+ ? SnapshotMetadata::Type::kFullyCustomized
1116
+ : SnapshotMetadata::Type::kDefault ;
1039
1117
Isolate* isolate = setup->isolate ();
1040
1118
Environment* env = setup->env ();
1041
1119
SnapshotCreator* creator = setup->snapshot_creator ();
@@ -1098,8 +1176,10 @@ ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out,
1098
1176
}
1099
1177
1100
1178
// Must be out of HandleScope
1101
- out->v8_snapshot_blob_data =
1102
- creator->CreateBlob (SnapshotCreator::FunctionCodeHandling::kKeep );
1179
+ SnapshotCreator::FunctionCodeHandling handling =
1180
+ WithoutCodeCache (*config) ? SnapshotCreator::FunctionCodeHandling::kClear
1181
+ : SnapshotCreator::FunctionCodeHandling::kKeep ;
1182
+ out->v8_snapshot_blob_data = creator->CreateBlob (handling);
1103
1183
1104
1184
// We must be able to rehash the blob when we restore it or otherwise
1105
1185
// the hash seed would be fixed by V8, introducing a vulnerability.
@@ -1111,7 +1191,8 @@ ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out,
1111
1191
per_process::metadata.versions .node ,
1112
1192
per_process::metadata.arch ,
1113
1193
per_process::metadata.platform ,
1114
- v8::ScriptCompiler::CachedDataVersionTag ()};
1194
+ v8::ScriptCompiler::CachedDataVersionTag (),
1195
+ config->flags };
1115
1196
1116
1197
// We cannot resurrect the handles from the snapshot, so make sure that
1117
1198
// no handles are left open in the environment after the blob is created
@@ -1132,21 +1213,22 @@ ExitCode SnapshotBuilder::GenerateAsSource(
1132
1213
const char * out_path,
1133
1214
const std::vector<std::string>& args,
1134
1215
const std::vector<std::string>& exec_args,
1135
- std::optional<std::string_view> main_script_path ,
1216
+ const SnapshotConfig& config ,
1136
1217
bool use_array_literals) {
1137
- std::string main_script_content;
1138
- std::optional<std::string_view> main_script_optional;
1139
- if (main_script_path.has_value ()) {
1140
- int r = ReadFileSync (&main_script_content, main_script_path.value ().data ());
1218
+ std::string builder_script_content;
1219
+ std::optional<std::string_view> builder_script_optional;
1220
+ if (config.builder_script_path .has_value ()) {
1221
+ std::string_view builder_script_path = config.builder_script_path .value ();
1222
+ int r = ReadFileSync (&builder_script_content, builder_script_path.data ());
1141
1223
if (r != 0 ) {
1142
1224
FPrintF (stderr,
1143
1225
" Cannot read main script %s for building snapshot. %s: %s" ,
1144
- main_script_path. value () ,
1226
+ builder_script_path ,
1145
1227
uv_err_name (r),
1146
1228
uv_strerror (r));
1147
1229
return ExitCode::kGenericUserError ;
1148
1230
}
1149
- main_script_optional = main_script_content ;
1231
+ builder_script_optional = builder_script_content ;
1150
1232
}
1151
1233
1152
1234
std::ofstream out (out_path, std::ios::out | std::ios::binary);
@@ -1156,7 +1238,8 @@ ExitCode SnapshotBuilder::GenerateAsSource(
1156
1238
}
1157
1239
1158
1240
SnapshotData data;
1159
- ExitCode exit_code = Generate (&data, args, exec_args, main_script_optional);
1241
+ ExitCode exit_code =
1242
+ Generate (&data, args, exec_args, builder_script_optional, config);
1160
1243
if (exit_code != ExitCode::kNoFailure ) {
1161
1244
return exit_code;
1162
1245
}
0 commit comments