@@ -110,6 +110,19 @@ size_t SeaSerializer::Write(const SeaResource& sea) {
110
110
written_total +=
111
111
WriteStringView (sea.code_cache .value (), StringLogMode::kAddressOnly );
112
112
}
113
+
114
+ if (!sea.assets .empty ()) {
115
+ Debug (" Write SEA resource assets size %zu\n " , sea.assets .size ());
116
+ written_total += WriteArithmetic<size_t >(sea.assets .size ());
117
+ for (auto const & [key, content] : sea.assets ) {
118
+ Debug (" Write SEA resource asset %s at %p, size=%zu\n " ,
119
+ key,
120
+ content.data (),
121
+ content.size ());
122
+ written_total += WriteStringView (key, StringLogMode::kAddressAndContent );
123
+ written_total += WriteStringView (content, StringLogMode::kAddressOnly );
124
+ }
125
+ }
113
126
return written_total;
114
127
}
115
128
@@ -157,7 +170,22 @@ SeaResource SeaDeserializer::Read() {
157
170
code_cache.data (),
158
171
code_cache.size ());
159
172
}
160
- return {flags, code_path, code, code_cache};
173
+
174
+ std::map<std::string_view, std::string_view> assets;
175
+ if (static_cast <bool >(flags & SeaFlags::kIncludeAssets )) {
176
+ size_t assets_size = ReadArithmetic<size_t >();
177
+ Debug (" Read SEA resource assets size %zu\n " , assets_size);
178
+ for (size_t i = 0 ; i < assets_size; ++i) {
179
+ std::string_view key = ReadStringView (StringLogMode::kAddressAndContent );
180
+ std::string_view content = ReadStringView (StringLogMode::kAddressOnly );
181
+ Debug (" Read SEA resource asset %s at %p, size=%zu\n " ,
182
+ key,
183
+ content.data (),
184
+ content.size ());
185
+ assets.emplace (key, content);
186
+ }
187
+ }
188
+ return {flags, code_path, code, code_cache, assets};
161
189
}
162
190
163
191
std::string_view FindSingleExecutableBlob () {
@@ -298,6 +326,7 @@ struct SeaConfig {
298
326
std::string main_path;
299
327
std::string output_path;
300
328
SeaFlags flags = SeaFlags::kDefault ;
329
+ std::map<std::string, std::string> assets;
301
330
};
302
331
303
332
std::optional<SeaConfig> ParseSingleExecutableConfig (
@@ -371,6 +400,18 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
371
400
result.flags |= SeaFlags::kUseCodeCache ;
372
401
}
373
402
403
+ std::optional<std::map<std::string, std::string>> assets_opt =
404
+ parser.GetTopLevelDictOfStrings (" assets" );
405
+ if (!assets_opt.has_value ()) {
406
+ FPrintF (stderr,
407
+ " \" assets\" field of %s is not a map of strings\n " ,
408
+ config_path);
409
+ return std::nullopt;
410
+ } else if (!assets_opt.value ().empty ()) {
411
+ result.flags |= SeaFlags::kIncludeAssets ;
412
+ result.assets = std::move (assets_opt.value ());
413
+ }
414
+
374
415
return result;
375
416
}
376
417
@@ -464,6 +505,21 @@ std::optional<std::string> GenerateCodeCache(std::string_view main_path,
464
505
return code_cache;
465
506
}
466
507
508
+ int BuildAssets (const std::map<std::string, std::string>& config,
509
+ std::map<std::string, std::string>* assets) {
510
+ for (auto const & [key, path] : config) {
511
+ std::string blob;
512
+ int r = ReadFileSync (&blob, path.c_str ());
513
+ if (r != 0 ) {
514
+ const char * err = uv_strerror (r);
515
+ FPrintF (stderr, " Cannot read asset %s: %s\n " , path.c_str (), err);
516
+ return r;
517
+ }
518
+ assets->emplace (key, std::move (blob));
519
+ }
520
+ return 0 ;
521
+ }
522
+
467
523
ExitCode GenerateSingleExecutableBlob (
468
524
const SeaConfig& config,
469
525
const std::vector<std::string>& args,
@@ -506,13 +562,22 @@ ExitCode GenerateSingleExecutableBlob(
506
562
}
507
563
}
508
564
565
+ std::map<std::string, std::string> assets;
566
+ if (!config.assets .empty () && BuildAssets (config.assets , &assets) != 0 ) {
567
+ return ExitCode::kGenericUserError ;
568
+ }
569
+ std::map<std::string_view, std::string_view> assets_view;
570
+ for (auto const & [key, content] : assets) {
571
+ assets_view.emplace (key, content);
572
+ }
509
573
SeaResource sea{
510
574
config.flags ,
511
575
config.main_path ,
512
576
builds_snapshot_from_main
513
577
? std::string_view{snapshot_blob.data (), snapshot_blob.size ()}
514
578
: std::string_view{main_script.data (), main_script.size ()},
515
- optional_sv_code_cache};
579
+ optional_sv_code_cache,
580
+ assets_view};
516
581
517
582
SeaSerializer serializer;
518
583
serializer.Write (sea);
@@ -547,6 +612,29 @@ ExitCode BuildSingleExecutableBlob(const std::string& config_path,
547
612
return ExitCode::kGenericUserError ;
548
613
}
549
614
615
+ void GetAsset (const FunctionCallbackInfo<Value>& args) {
616
+ CHECK_EQ (args.Length (), 1 );
617
+ CHECK (args[0 ]->IsString ());
618
+ Utf8Value key (args.GetIsolate (), args[0 ]);
619
+ SeaResource sea_resource = FindSingleExecutableResource ();
620
+ if (sea_resource.assets .empty ()) {
621
+ return ;
622
+ }
623
+ auto it = sea_resource.assets .find (*key);
624
+ if (it == sea_resource.assets .end ()) {
625
+ return ;
626
+ }
627
+ // We cast away the constness here, the JS land should ensure that
628
+ // the data is not mutated.
629
+ std::unique_ptr<v8::BackingStore> store = ArrayBuffer::NewBackingStore (
630
+ const_cast <char *>(it->second .data ()),
631
+ it->second .size (),
632
+ [](void *, size_t , void *) {},
633
+ nullptr );
634
+ Local<ArrayBuffer> ab = ArrayBuffer::New (args.GetIsolate (), std::move (store));
635
+ args.GetReturnValue ().Set (ab);
636
+ }
637
+
550
638
void Initialize (Local<Object> target,
551
639
Local<Value> unused,
552
640
Local<Context> context,
@@ -558,13 +646,15 @@ void Initialize(Local<Object> target,
558
646
IsExperimentalSeaWarningNeeded);
559
647
SetMethod (context, target, " getCodePath" , GetCodePath);
560
648
SetMethod (context, target, " getCodeCache" , GetCodeCache);
649
+ SetMethod (context, target, " getAsset" , GetAsset);
561
650
}
562
651
563
652
void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
564
653
registry->Register (IsSea);
565
654
registry->Register (IsExperimentalSeaWarningNeeded);
566
655
registry->Register (GetCodePath);
567
656
registry->Register (GetCodeCache);
657
+ registry->Register (GetAsset);
568
658
}
569
659
570
660
} // namespace sea
0 commit comments