|
1 | 1 |
|
2 | 2 | #include "node_snapshotable.h" |
| 3 | +#include <fstream> |
3 | 4 | #include <iostream> |
4 | 5 | #include <sstream> |
5 | 6 | #include <vector> |
@@ -715,13 +716,6 @@ SnapshotData::~SnapshotData() { |
715 | 716 | } |
716 | 717 | } |
717 | 718 |
|
718 | | -template <typename T> |
719 | | -void WriteVector(std::ostream* ss, const T* vec, size_t size) { |
720 | | - for (size_t i = 0; i < size; i++) { |
721 | | - *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ','); |
722 | | - } |
723 | | -} |
724 | | - |
725 | 719 | static std::string GetCodeCacheDefName(const std::string& id) { |
726 | 720 | char buf[64] = {0}; |
727 | 721 | size_t size = id.size(); |
@@ -746,48 +740,71 @@ static std::string FormatSize(size_t size) { |
746 | 740 | return buf; |
747 | 741 | } |
748 | 742 |
|
749 | | -#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS |
750 | | -static void WriteDataAsCharString(std::ostream* ss, |
751 | | - const uint8_t* data, |
752 | | - size_t length) { |
753 | | - for (size_t i = 0; i < length; i++) { |
754 | | - const uint8_t ch = data[i]; |
755 | | - // We can print most printable characters directly. The exceptions are '\' |
756 | | - // (escape characters), " (would end the string), and ? (trigraphs). The |
757 | | - // latter may be overly conservative: we compile with C++17 which doesn't |
758 | | - // support trigraphs. |
759 | | - if (ch >= ' ' && ch <= '~' && ch != '\\' && ch != '"' && ch != '?') { |
760 | | - *ss << ch; |
761 | | - } else { |
762 | | - // All other characters are blindly output as octal. |
763 | | - const char c0 = '0' + ((ch >> 6) & 7); |
764 | | - const char c1 = '0' + ((ch >> 3) & 7); |
765 | | - const char c2 = '0' + (ch & 7); |
766 | | - *ss << "\\" << c0 << c1 << c2; |
767 | | - } |
768 | | - if (i % 64 == 63) { |
769 | | - // Go to a newline every 64 bytes since many text editors have |
770 | | - // problems with very long lines. |
771 | | - *ss << "\"\n\""; |
772 | | - } |
| 743 | +std::string ToOctalString(const uint8_t ch) { |
| 744 | + // We can print most printable characters directly. The exceptions are '\' |
| 745 | + // (escape characters), " (would end the string), and ? (trigraphs). The |
| 746 | + // latter may be overly conservative: we compile with C++17 which doesn't |
| 747 | + // support trigraphs. |
| 748 | + if (ch >= ' ' && ch <= '~' && ch != '\\' && ch != '"' && ch != '?') { |
| 749 | + return std::string(1, static_cast<char>(ch)); |
773 | 750 | } |
| 751 | + // All other characters are blindly output as octal. |
| 752 | + const char c0 = '0' + ((ch >> 6) & 7); |
| 753 | + const char c1 = '0' + ((ch >> 3) & 7); |
| 754 | + const char c2 = '0' + (ch & 7); |
| 755 | + return std::string("\\") + c0 + c1 + c2; |
774 | 756 | } |
775 | 757 |
|
776 | | -static void WriteStaticCodeCacheDataAsStringLiteral( |
777 | | - std::ostream* ss, const builtins::CodeCacheInfo& info) { |
778 | | - *ss << "static const uint8_t *" << GetCodeCacheDefName(info.id) |
779 | | - << "= reinterpret_cast<const uint8_t *>(\""; |
780 | | - WriteDataAsCharString(ss, info.data.data, info.data.length); |
781 | | - *ss << "\");\n"; |
| 758 | +std::vector<std::string> GetOctalTable() { |
| 759 | + size_t size = 1 << 8; |
| 760 | + std::vector<std::string> code_table(size); |
| 761 | + for (size_t i = 0; i < size; ++i) { |
| 762 | + code_table[i] = ToOctalString(static_cast<uint8_t>(i)); |
| 763 | + } |
| 764 | + return code_table; |
782 | 765 | } |
783 | | -#else |
784 | | -static void WriteStaticCodeCacheDataAsArray( |
785 | | - std::ostream* ss, const builtins::CodeCacheInfo& info) { |
786 | | - *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n"; |
787 | | - WriteVector(ss, info.data.data, info.data.length); |
788 | | - *ss << "};\n"; |
| 766 | + |
| 767 | +const std::string& GetOctalCode(uint8_t index) { |
| 768 | + static std::vector<std::string> table = GetOctalTable(); |
| 769 | + return table[index]; |
| 770 | +} |
| 771 | + |
| 772 | +template <typename T> |
| 773 | +void WriteByteVectorLiteral(std::ostream* ss, |
| 774 | + const T* vec, |
| 775 | + size_t size, |
| 776 | + const char* var_name, |
| 777 | + bool use_string_literals) { |
| 778 | + constexpr bool is_uint8_t = std::is_same_v<T, uint8_t>; |
| 779 | + static_assert(is_uint8_t || std::is_same_v<T, char>); |
| 780 | + constexpr const char* type_name = is_uint8_t ? "uint8_t" : "char"; |
| 781 | + if (use_string_literals) { |
| 782 | + const uint8_t* data = reinterpret_cast<const uint8_t*>(vec); |
| 783 | + *ss << "static const " << type_name << " *" << var_name << " = "; |
| 784 | + *ss << (is_uint8_t ? R"(reinterpret_cast<const uint8_t *>(")" : "\""); |
| 785 | + for (size_t i = 0; i < size; i++) { |
| 786 | + const uint8_t ch = data[i]; |
| 787 | + *ss << GetOctalCode(ch); |
| 788 | + if (i % 64 == 63) { |
| 789 | + // Go to a newline every 64 bytes since many text editors have |
| 790 | + // problems with very long lines. |
| 791 | + *ss << "\"\n\""; |
| 792 | + } |
| 793 | + } |
| 794 | + *ss << (is_uint8_t ? "\");\n" : "\";\n"); |
| 795 | + } else { |
| 796 | + *ss << "static const " << type_name << " " << var_name << "[] = {"; |
| 797 | + for (size_t i = 0; i < size; i++) { |
| 798 | + *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ','); |
| 799 | + if (i % 64 == 63) { |
| 800 | + // Print a newline every 64 units and a offset to improve |
| 801 | + // readability. |
| 802 | + *ss << " // " << (i / 64) << "\n"; |
| 803 | + } |
| 804 | + } |
| 805 | + *ss << "};\n"; |
| 806 | + } |
789 | 807 | } |
790 | | -#endif |
791 | 808 |
|
792 | 809 | static void WriteCodeCacheInitializer(std::ostream* ss, |
793 | 810 | const std::string& id, |
@@ -800,7 +817,9 @@ static void WriteCodeCacheInitializer(std::ostream* ss, |
800 | 817 | *ss << " },\n"; |
801 | 818 | } |
802 | 819 |
|
803 | | -void FormatBlob(std::ostream& ss, const SnapshotData* data) { |
| 820 | +void FormatBlob(std::ostream& ss, |
| 821 | + const SnapshotData* data, |
| 822 | + bool use_string_literals) { |
804 | 823 | ss << R"(#include <cstddef> |
805 | 824 | #include "env.h" |
806 | 825 | #include "node_snapshot_builder.h" |
@@ -811,32 +830,24 @@ void FormatBlob(std::ostream& ss, const SnapshotData* data) { |
811 | 830 | namespace node { |
812 | 831 | )"; |
813 | 832 |
|
814 | | -#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS |
815 | | - ss << R"(static const char *v8_snapshot_blob_data = ")"; |
816 | | - WriteDataAsCharString( |
817 | | - &ss, |
818 | | - reinterpret_cast<const uint8_t*>(data->v8_snapshot_blob_data.data), |
819 | | - data->v8_snapshot_blob_data.raw_size); |
820 | | - ss << R"(";)"; |
821 | | -#else |
822 | | - ss << R"(static const char v8_snapshot_blob_data[] = {)"; |
823 | | - WriteVector(&ss, |
824 | | - data->v8_snapshot_blob_data.data, |
825 | | - data->v8_snapshot_blob_data.raw_size); |
826 | | - ss << R"(};)"; |
827 | | -#endif |
| 833 | + WriteByteVectorLiteral(&ss, |
| 834 | + data->v8_snapshot_blob_data.data, |
| 835 | + data->v8_snapshot_blob_data.raw_size, |
| 836 | + "v8_snapshot_blob_data", |
| 837 | + use_string_literals); |
828 | 838 |
|
829 | 839 | ss << R"(static const int v8_snapshot_blob_size = )" |
830 | | - << data->v8_snapshot_blob_data.raw_size << ";"; |
| 840 | + << data->v8_snapshot_blob_data.raw_size << ";\n"; |
831 | 841 |
|
| 842 | + // Windows can't deal with too many large vector initializers. |
| 843 | + // Store the data into static arrays first. |
832 | 844 | for (const auto& item : data->code_cache) { |
833 | | -#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS |
834 | | - WriteStaticCodeCacheDataAsStringLiteral(&ss, item); |
835 | | -#else |
836 | | - // Windows can't deal with too many large vector initializers. |
837 | | - // Store the data into static arrays first. |
838 | | - WriteStaticCodeCacheDataAsArray(&ss, item); |
839 | | -#endif |
| 845 | + std::string var_name = GetCodeCacheDefName(item.id); |
| 846 | + WriteByteVectorLiteral(&ss, |
| 847 | + item.data.data, |
| 848 | + item.data.length, |
| 849 | + var_name.c_str(), |
| 850 | + use_string_literals); |
840 | 851 | } |
841 | 852 |
|
842 | 853 | ss << R"(const SnapshotData snapshot_data { |
@@ -1073,17 +1084,45 @@ ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out, |
1073 | 1084 | return ExitCode::kNoFailure; |
1074 | 1085 | } |
1075 | 1086 |
|
1076 | | -ExitCode SnapshotBuilder::Generate( |
1077 | | - std::ostream& out, |
| 1087 | +ExitCode SnapshotBuilder::GenerateAsSource( |
| 1088 | + const char* out_path, |
1078 | 1089 | const std::vector<std::string>& args, |
1079 | 1090 | const std::vector<std::string>& exec_args, |
1080 | | - std::optional<std::string_view> main_script) { |
| 1091 | + std::optional<std::string_view> main_script_path, |
| 1092 | + bool use_string_literals) { |
| 1093 | + std::string main_script_content; |
| 1094 | + std::optional<std::string_view> main_script_optional; |
| 1095 | + if (main_script_path.has_value()) { |
| 1096 | + int r = ReadFileSync(&main_script_content, main_script_path.value().data()); |
| 1097 | + if (r != 0) { |
| 1098 | + FPrintF(stderr, |
| 1099 | + "Cannot read main script %s for building snapshot. %s: %s", |
| 1100 | + main_script_path.value(), |
| 1101 | + uv_err_name(r), |
| 1102 | + uv_strerror(r)); |
| 1103 | + return ExitCode::kGenericUserError; |
| 1104 | + } |
| 1105 | + main_script_optional = main_script_content; |
| 1106 | + } |
| 1107 | + |
| 1108 | + std::ofstream out(out_path, std::ios::out | std::ios::binary); |
| 1109 | + if (!out) { |
| 1110 | + FPrintF(stderr, "Cannot open %s for output.\n", out_path); |
| 1111 | + return ExitCode::kGenericUserError; |
| 1112 | + } |
| 1113 | + |
1081 | 1114 | SnapshotData data; |
1082 | | - ExitCode exit_code = Generate(&data, args, exec_args, main_script); |
| 1115 | + ExitCode exit_code = Generate(&data, args, exec_args, main_script_optional); |
1083 | 1116 | if (exit_code != ExitCode::kNoFailure) { |
1084 | 1117 | return exit_code; |
1085 | 1118 | } |
1086 | | - FormatBlob(out, &data); |
| 1119 | + FormatBlob(out, &data, use_string_literals); |
| 1120 | + |
| 1121 | + if (!out) { |
| 1122 | + std::cerr << "Failed to write to " << out_path << "\n"; |
| 1123 | + exit_code = node::ExitCode::kGenericUserError; |
| 1124 | + } |
| 1125 | + |
1087 | 1126 | return exit_code; |
1088 | 1127 | } |
1089 | 1128 |
|
|