1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef INSPECTOR_UTILS_H
17 #define INSPECTOR_UTILS_H
18 #include <cstring>
19 #include <securec.h>
20 #include <sstream>
21 #include <string>
22
23 #include "jsvm_dfx.h"
24 #include "jsvm_util.h"
25 #include "uv.h"
26 #include "v8-inspector.h"
27
28 #ifdef __GNUC__
29 #define MUST_USE_RESULT __attribute__((warn_unused_result))
30 #else
31 #define MUST_USE_RESULT
32 #endif
33
34 namespace jsvm {
35 namespace inspector {
36 template<typename Inner, typename Outer>
37 class ContainerOfHelper {
38 public:
39 // The helper is for doing safe downcasts from base types to derived types.
40 inline ContainerOfHelper(Inner Outer::* field, Inner* pointer);
41 template<typename TypeName>
42 inline operator TypeName*() const;
43
44 private:
45 Outer* const pointer;
46 };
47
ToLower(char c)48 inline char ToLower(char c)
49 {
50 return std::tolower(c, std::locale::classic());
51 }
52
StringEqualNoCase(const char * a,const char * b)53 inline bool StringEqualNoCase(const char* a, const char* b)
54 {
55 while (ToLower(*a) == ToLower(*b++)) {
56 if (*a++ == '\0') {
57 return true;
58 }
59 }
60 return false;
61 }
62
StringEqualNoCaseN(const char * a,const char * b,size_t length)63 inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length)
64 {
65 for (size_t i = 0; i < length; i++) {
66 if (ToLower(a[i]) != ToLower(b[i])) {
67 return false;
68 }
69 if (a[i] == '\0') {
70 return true;
71 }
72 }
73 return true;
74 }
75
76 // Use this when a variable or parameter is unused in order to explicitly
77 // silence a compiler warning about that.
78 template<typename T>
USE(T &&)79 inline void USE(T&&)
80 {}
81
82 template<typename T, void (*function)(T*)>
83 struct FunctionDeleter {
operatorFunctionDeleter84 void operator()(T* pointer) const
85 {
86 function(pointer);
87 }
88 typedef std::unique_ptr<T, FunctionDeleter> Pointer;
89 };
90
91 template<typename T, void (*function)(T*)>
92 using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
93
94 template<typename Inner, typename Outer>
OffsetOf(Inner Outer::* field)95 constexpr uintptr_t OffsetOf(Inner Outer::* field)
96 {
97 return reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(nullptr)->*field));
98 }
99
100 template<typename Inner, typename Outer>
ContainerOfHelper(Inner Outer::* field,Inner * pointer)101 ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::* field, Inner* pointer)
102 : pointer(reinterpret_cast<Outer*>(reinterpret_cast<uintptr_t>(pointer) - OffsetOf(field)))
103 {}
104
105 template<typename Inner, typename Outer>
106 template<typename TypeName>
107 ContainerOfHelper<Inner, Outer>::operator TypeName*() const
108 {
109 return static_cast<TypeName*>(pointer);
110 }
111
112 // Calculate the address of the outer (i.e. embedding) struct from
113 // the interior pointer to a data member.
114 template<typename Inner, typename Outer>
ContainerOf(Inner Outer::* field,Inner * pointer)115 constexpr ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::* field, Inner* pointer)
116 {
117 return ContainerOfHelper<Inner, Outer>(field, pointer);
118 }
119
120 template<typename T, size_t kStackStorageSize = 1024>
121 class MaybeStackBuffer {
122 public:
Out()123 const T* Out() const
124 {
125 return buf;
126 }
127
Out()128 T* Out()
129 {
130 return buf;
131 }
132
133 // operator* for compatibility with `v8::String::(Utf8)Value`
134 T* operator*()
135 {
136 return buf;
137 }
138
139 const T* operator*() const
140 {
141 return buf;
142 }
143
144 T& operator[](size_t index)
145 {
146 CHECK_LT(index, GetLength());
147 return buf[index];
148 }
149
150 const T& operator[](size_t index) const
151 {
152 CHECK_LT(index, GetLength());
153 return buf[index];
154 }
155
GetLength()156 size_t GetLength() const
157 {
158 return length;
159 }
160
161 // Current maximum capacity of the buffer with which SetLength() can be used
162 // without first calling AllocateSufficientStorage().
GetCapacity()163 size_t GetCapacity() const
164 {
165 return capacity;
166 }
167
168 void AllocateSufficientStorage(size_t storage);
169
SetLength(size_t lengthParam)170 void SetLength(size_t lengthParam)
171 {
172 // GetCapacity() returns how much memory is actually available.
173 CHECK_LE(lengthParam, GetCapacity());
174 length = lengthParam;
175 }
176
SetLengthAndZeroTerminate(size_t len)177 void SetLengthAndZeroTerminate(size_t len)
178 {
179 // GetCapacity() returns how much memory is actually available.
180 CHECK_LE(len + 1, GetCapacity());
181 SetLength(len);
182
183 // T() is 0 for integer types, nullptr for pointers, etc.
184 buf[len] = T();
185 }
186
Invalidate()187 void Invalidate()
188 {
189 CHECK(!IsAllocated());
190 capacity = 0;
191 length = 0;
192 buf = nullptr;
193 }
194
195 // If the buffer is stored in the heap rather than on the stack.
IsAllocated()196 bool IsAllocated() const
197 {
198 return !IsInvalidated() && buf != bufSt;
199 }
200
201 // If Invalidate() has been called.
IsInvalidated()202 bool IsInvalidated() const
203 {
204 return buf == nullptr;
205 }
206
207 // Release ownership of the malloc'd buffer.
208 // Note: This does not free the buffer.
Release()209 void Release()
210 {
211 CHECK(IsAllocated());
212 buf = bufSt;
213 length = 0;
214 capacity = jsvm::ArraySize(bufSt);
215 }
216
MaybeStackBuffer()217 MaybeStackBuffer() : length(0), capacity(jsvm::ArraySize(bufSt)), buf(bufSt)
218 {
219 // Default to a zero-length, null-terminated buffer.
220 buf[0] = T();
221 }
222
MaybeStackBuffer(size_t storage)223 explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer()
224 {
225 AllocateSufficientStorage(storage);
226 }
227
~MaybeStackBuffer()228 ~MaybeStackBuffer()
229 {
230 if (IsAllocated()) {
231 free(buf);
232 }
233 }
234
ToString()235 inline std::basic_string<T> ToString() const
236 {
237 return { Out(), GetLength() };
238 }
ToStringView()239 inline std::basic_string_view<T> ToStringView() const
240 {
241 return { Out(), GetLength() };
242 }
243
244 private:
245 size_t length;
246 // capacity of the malloc'ed buf
247 size_t capacity;
248 T* buf;
249 T bufSt[kStackStorageSize];
250 };
251
252 class TwoByteValue : public MaybeStackBuffer<uint16_t> {
253 public:
254 explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
255 };
256
257 template<typename T>
Realloc(T * pointer,size_t n,size_t oldN)258 T* Realloc(T* pointer, size_t n, size_t oldN)
259 {
260 CHECK(n > 0);
261 size_t newSize = sizeof(T) * n;
262 T* newPtr = static_cast<T*>(malloc(newSize));
263
264 CHECK_NOT_NULL(newPtr);
265
266 if (!pointer) {
267 return newPtr;
268 }
269
270 size_t oldSize = sizeof(T) * oldN;
271 CHECK(newSize > oldSize);
272 errno_t ret = memcpy_s(newPtr, newSize, pointer, oldSize);
273 CHECK(ret == EOK);
274 free(pointer);
275
276 return newPtr;
277 }
278
279 template<typename T, size_t kStackStorageSize>
AllocateSufficientStorage(size_t storage)280 void MaybeStackBuffer<T, kStackStorageSize>::AllocateSufficientStorage(size_t storage)
281 {
282 CHECK(!IsInvalidated());
283 if (storage > GetCapacity()) {
284 bool wasAllocated = IsAllocated();
285 T* allocatedPtr = wasAllocated ? buf : nullptr;
286 buf = Realloc(allocatedPtr, storage, length);
287 capacity = storage;
288 if (!wasAllocated && length > 0) {
289 int ret = memcpy_s(buf, length * sizeof(buf[0]), bufSt, length * sizeof(buf[0]));
290 CHECK(ret == EOK);
291 }
292 }
293
294 length = storage;
295 }
296
297 // Convertion between v8_inspector::StringView and std::string
298 std::string StringViewToUtf8(v8_inspector::StringView view);
299 std::unique_ptr<v8_inspector::StringBuffer> Utf8ToStringView(const std::string_view message);
300
301 constexpr size_t TO_TRANSFORM_CHAR_NUM = 3;
302 constexpr size_t TRANSFORMED_CHAR_NUM = 4;
303
304 enum ByteOffset : uint8_t {
305 BYTE_0 = 0,
306 BYTE_1 = 1,
307 BYTE_2 = 2,
308 BYTE_3 = 3,
309 BYTE_4 = 4,
310 BYTE_5 = 5,
311 BYTE_6 = 6,
312 BYTE_7 = 7,
313 BIT_0 = 0,
314 BIT_1 = 1,
315 BIT_2 = 2,
316 BIT_3 = 3,
317 BIT_4 = 4,
318 BIT_5 = 5,
319 BIT_6 = 6,
320 BIT_7 = 7,
321 BIT_8 = 8,
322 };
323
324 // Encode base64
Base64EncodeSize(size_t size)325 inline constexpr size_t Base64EncodeSize(size_t size)
326 {
327 return ((size + TO_TRANSFORM_CHAR_NUM - 1) / TO_TRANSFORM_CHAR_NUM * TRANSFORMED_CHAR_NUM);
328 }
329
330 // Be careful: If dlen is less than expected encode size, it will crash.
331 size_t Base64Encode(const char* inputString, size_t slen, char* outputBuffer, size_t dlen);
332
333 std::string GetHumanReadableProcessName();
334
335 // Either succeeds with exactly |length| bytes of cryptographically
336 // strong pseudo-random data, or fails. This function may block.
337 // Don't assume anything about the contents of |buffer| on error.
338 // As a special case, |length == 0| can be used to check if the CSPRNG
339 // is properly seeded without consuming entropy.
340 MUST_USE_RESULT bool CSPRNG(void* buffer, size_t length);
341
342 void CheckedUvLoopClose(uv_loop_t* loop);
343 } // namespace inspector
344 } // namespace jsvm
345 #endif
346