/src/hermes/lib/BCGen/HBC/SerializedLiteralGenerator.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "hermes/BCGen/HBC/SerializedLiteralGenerator.h" |
9 | | #include "hermes/BCGen/HBC/BytecodeGenerator.h" |
10 | | #include "llvh/Support/Endian.h" |
11 | | |
12 | | namespace hermes { |
13 | | namespace hbc { |
14 | | |
15 | | namespace { |
16 | | void appendTagToBuffer( |
17 | | std::vector<unsigned char> &buff, |
18 | | SerializedLiteralGenerator::TagType tag, |
19 | 77 | int seqLength) { |
20 | 77 | if (seqLength > 15) { |
21 | 12 | buff.push_back((tag | 0x80) | (seqLength >> 8)); |
22 | 12 | buff.push_back(seqLength & 0xffff); |
23 | 65 | } else { |
24 | 65 | buff.push_back(tag + seqLength); |
25 | 65 | } |
26 | 77 | } |
27 | | |
28 | | /// Write a value to a vector of characters in little-endian format. |
29 | | template <typename value_type> |
30 | | void serializeValueToBuffer( |
31 | | value_type value, |
32 | 33.3k | std::vector<unsigned char> &buff) { |
33 | | // Since endian::write takes a pointer, we can write directly to |
34 | | // the buffer. First we resize it to make sure the data fits, then |
35 | | // we pass a pointer to the original end of the vector. |
36 | | // Since the buffer is a char buffer, we pass in an alignment of 1 |
37 | | // to endian::write. |
38 | 33.3k | buff.resize(buff.size() + sizeof(value_type)); |
39 | 33.3k | llvh::support::endian::write<value_type, 1>( |
40 | 33.3k | buff.data() + buff.size() - sizeof(value_type), |
41 | 33.3k | value, |
42 | 33.3k | llvh::support::endianness::little); |
43 | 33.3k | } SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned int>(unsigned int, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&) Line | Count | Source | 32 | 33.3k | std::vector<unsigned char> &buff) { | 33 | | // Since endian::write takes a pointer, we can write directly to | 34 | | // the buffer. First we resize it to make sure the data fits, then | 35 | | // we pass a pointer to the original end of the vector. | 36 | | // Since the buffer is a char buffer, we pass in an alignment of 1 | 37 | | // to endian::write. | 38 | 33.3k | buff.resize(buff.size() + sizeof(value_type)); | 39 | 33.3k | llvh::support::endian::write<value_type, 1>( | 40 | 33.3k | buff.data() + buff.size() - sizeof(value_type), | 41 | 33.3k | value, | 42 | 33.3k | llvh::support::endianness::little); | 43 | 33.3k | } |
Unexecuted instantiation: SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned short>(unsigned short, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&) Unexecuted instantiation: SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned char>(unsigned char, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&) |
44 | | |
45 | | template <> |
46 | 2 | void serializeValueToBuffer(double value, std::vector<unsigned char> &buff) { |
47 | | // Since endian::write takes a pointer, we can write directly to |
48 | | // the buffer. First we resize it to make sure the data fits, then |
49 | | // we pass a pointer to the original end of the vector. |
50 | | // Since the buffer is a char buffer, we pass in an alignment of 1 |
51 | | // to endian::write. |
52 | 2 | buff.resize(buff.size() + sizeof(double)); |
53 | | // NaN may have a different bit pattern on different platforms. |
54 | | // We need to make sure that the bit pattern is consistent. |
55 | 2 | if (LLVM_UNLIKELY(std::isnan(value))) { |
56 | 0 | static_assert( |
57 | 0 | sizeof(double) == sizeof(uint64_t), "double better be 64 bits"); |
58 | 0 | static constexpr uint64_t qnan = 0xfff8000000000000; |
59 | 0 | llvh::support::endian::write<uint64_t, 1>( |
60 | 0 | buff.data() + buff.size() - sizeof(uint64_t), |
61 | 0 | qnan, |
62 | 0 | llvh::support::endianness::little); |
63 | 2 | } else { |
64 | 2 | llvh::support::endian::write<double, 1>( |
65 | 2 | buff.data() + buff.size() - sizeof(double), |
66 | 2 | value, |
67 | 2 | llvh::support::endianness::little); |
68 | 2 | } |
69 | 2 | } |
70 | | } // namespace |
71 | | |
72 | | void SerializedLiteralGenerator::serializeBuffer( |
73 | | llvh::ArrayRef<Literal *> literals, |
74 | | std::vector<unsigned char> &buff, |
75 | 67 | bool isKeyBuffer) { |
76 | | // Stores the last type parsed by the for loop |
77 | 67 | TagType lastTag = NumberTag; |
78 | | |
79 | | // Stores the type currently being parsed by the for loop |
80 | 67 | TagType newTag = NumberTag; |
81 | | |
82 | | // Stores the values of each serialized Literal in a sequence so that |
83 | | // they can be added to tempBuff after the tag is finalized |
84 | 67 | std::vector<unsigned char> tmpSeqBuffer; |
85 | | |
86 | | // Stores the length of the current type sequence |
87 | 67 | size_t seqLength = 0; |
88 | | |
89 | 33.4k | for (size_t i = 0, buffSize = literals.size(); i < buffSize; ++i) { |
90 | | // The first switch simply sets newTag. |
91 | | // If newTag is different from lastTag, we append the tag and the |
92 | | // tmpSeqBuffer to buff. |
93 | | // After this check, we serialize the value to tmpSeqBuffer if possible. |
94 | | // Since we can only write the tmpSeqBuffer to buff once we know the |
95 | | // sequence ends, the check has to go after the type checking switch. |
96 | | // Since we can only write to tmpSeqBuffer after it's been cleared (if |
97 | | // the sequence is over), the only way to delay the write to tmpSeqBuffer |
98 | | // is to either write to a second temporary buffer, and write that to |
99 | | // tmpSeqBuffer afterwards, or add a second switch. |
100 | 33.3k | switch (literals[i]->getKind()) { |
101 | 33.3k | case ValueKind::LiteralNumberKind: |
102 | 33.3k | newTag = llvh::cast<LiteralNumber>(literals[i]) |
103 | 33.3k | ->isIntTypeRepresentible<int32_t>() |
104 | 33.3k | ? IntegerTag |
105 | 33.3k | : NumberTag; |
106 | 33.3k | break; |
107 | 0 | case ValueKind::LiteralStringKind: { |
108 | 0 | auto str = llvh::cast<LiteralString>(literals[i])->getValue().str(); |
109 | 0 | int ind = |
110 | 0 | isKeyBuffer ? BMGen_.getIdentifierID(str) : BMGen_.getStringID(str); |
111 | |
|
112 | 0 | if (ind > UINT16_MAX) { |
113 | 0 | newTag = LongStringTag; |
114 | 0 | } else if (ind > UINT8_MAX) { |
115 | 0 | newTag = ShortStringTag; |
116 | 0 | } else { |
117 | 0 | newTag = ByteStringTag; |
118 | 0 | } |
119 | 0 | break; |
120 | 0 | } |
121 | 0 | case ValueKind::LiteralBoolKind: |
122 | 0 | newTag = llvh::cast<LiteralBool>(literals[i])->getValue() ? TrueTag |
123 | 0 | : FalseTag; |
124 | 0 | break; |
125 | 0 | case ValueKind::LiteralNullKind: |
126 | 0 | newTag = NullTag; |
127 | 0 | break; |
128 | 0 | default: |
129 | 0 | hermes_fatal("Invalid Literal Kind"); |
130 | 33.3k | } |
131 | | |
132 | 33.3k | if (newTag != lastTag || seqLength == SequenceMax) { |
133 | 16 | if (seqLength > 0) { |
134 | 10 | appendTagToBuffer(buff, lastTag, seqLength); |
135 | 10 | buff.insert(buff.end(), tmpSeqBuffer.begin(), tmpSeqBuffer.end()); |
136 | 10 | tmpSeqBuffer.resize(0); |
137 | 10 | } |
138 | 16 | lastTag = newTag; |
139 | 16 | seqLength = 0; |
140 | 16 | } |
141 | 33.3k | seqLength++; |
142 | | |
143 | 33.3k | switch (literals[i]->getKind()) { |
144 | 33.3k | case ValueKind::LiteralNumberKind: { |
145 | 33.3k | auto litNum = llvh::cast<LiteralNumber>(literals[i]); |
146 | 33.3k | if (llvh::Optional<int> intPointer = |
147 | 33.3k | litNum->isIntTypeRepresentible<int32_t>()) { |
148 | 33.3k | int n = intPointer.getValue(); |
149 | 33.3k | serializeValueToBuffer<uint32_t>(n, tmpSeqBuffer); |
150 | 33.3k | } else { |
151 | 2 | double num = litNum->getValue(); |
152 | 2 | serializeValueToBuffer<double>(num, tmpSeqBuffer); |
153 | 2 | } |
154 | 33.3k | break; |
155 | 0 | } |
156 | 0 | case ValueKind::LiteralStringKind: { |
157 | | // For strings, we are going to store the index to the string table, |
158 | | // which will need to be decoded at runtime. |
159 | 0 | auto str = llvh::cast<LiteralString>(literals[i])->getValue().str(); |
160 | 0 | auto stringID = |
161 | 0 | isKeyBuffer ? BMGen_.getIdentifierID(str) : BMGen_.getStringID(str); |
162 | |
|
163 | 0 | if (stringID > UINT16_MAX) { |
164 | 0 | serializeValueToBuffer<uint32_t>(stringID, tmpSeqBuffer); |
165 | 0 | } else if (stringID > UINT8_MAX) { |
166 | 0 | serializeValueToBuffer<uint16_t>(stringID, tmpSeqBuffer); |
167 | 0 | } else { |
168 | 0 | serializeValueToBuffer<uint8_t>(stringID, tmpSeqBuffer); |
169 | 0 | } |
170 | 0 | break; |
171 | 0 | } |
172 | 0 | case ValueKind::LiteralBoolKind: |
173 | 0 | case ValueKind::LiteralNullKind: |
174 | | /* no-op */ |
175 | 0 | break; |
176 | 0 | default: |
177 | 0 | hermes_fatal("Invalid Literal Kind"); |
178 | 33.3k | } |
179 | 33.3k | } |
180 | | // The last value in the buffer can't get serialized in the loop. |
181 | 67 | appendTagToBuffer(buff, lastTag, seqLength); |
182 | 67 | buff.insert(buff.end(), tmpSeqBuffer.begin(), tmpSeqBuffer.end()); |
183 | 67 | } |
184 | | |
185 | | } // namespace hbc |
186 | | } // namespace hermes |