Line | Count | Source |
1 | | // Copyright Joyent, Inc. and other Node contributors. |
2 | | // |
3 | | // Permission is hereby granted, free of charge, to any person obtaining a |
4 | | // copy of this software and associated documentation files (the |
5 | | // "Software"), to deal in the Software without restriction, including |
6 | | // without limitation the rights to use, copy, modify, merge, publish, |
7 | | // distribute, sublicense, and/or sell copies of the Software, and to permit |
8 | | // persons to whom the Software is furnished to do so, subject to the |
9 | | // following conditions: |
10 | | // |
11 | | // The above copyright notice and this permission notice shall be included |
12 | | // in all copies or substantial portions of the Software. |
13 | | // |
14 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 | | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 | | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 | | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 | | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 | | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 | | // USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | | |
22 | | #include "util.h" // NOLINT(build/include_inline) |
23 | | #include <cmath> |
24 | | #include <cstdint> |
25 | | #include "util-inl.h" |
26 | | |
27 | | #include "debug_utils-inl.h" |
28 | | #include "env-inl.h" |
29 | | #include "node_buffer.h" |
30 | | #include "node_errors.h" |
31 | | #include "node_internals.h" |
32 | | #include "node_snapshot_builder.h" |
33 | | #include "node_v8_platform-inl.h" |
34 | | #include "string_bytes.h" |
35 | | #include "v8-value.h" |
36 | | |
37 | | #ifdef _WIN32 |
38 | | #include <io.h> // _S_IREAD _S_IWRITE |
39 | | #include <time.h> |
40 | | #ifndef S_IRUSR |
41 | | #define S_IRUSR _S_IREAD |
42 | | #endif // S_IRUSR |
43 | | #ifndef S_IWUSR |
44 | | #define S_IWUSR _S_IWRITE |
45 | | #endif // S_IWUSR |
46 | | #else |
47 | | #include <sys/time.h> |
48 | | #include <sys/types.h> |
49 | | #endif |
50 | | |
51 | | #include <simdutf.h> |
52 | | |
53 | | #include <atomic> |
54 | | #include <cstdio> |
55 | | #include <cstring> |
56 | | #include <iomanip> |
57 | | #include <sstream> |
58 | | |
59 | | static std::atomic_int seq = {0}; // Sequence number for diagnostic filenames. |
60 | | |
61 | | // F_OK etc. constants |
62 | | #ifdef _WIN32 |
63 | | #include "uv.h" |
64 | | #else |
65 | | #include <unistd.h> |
66 | | #endif |
67 | | |
68 | | // The access modes can be any of F_OK, R_OK, W_OK or X_OK. Some might not be |
69 | | // available on specific systems. They can be used in combination as well |
70 | | // (F_OK | R_OK | W_OK | X_OK). |
71 | | constexpr int kMaximumAccessMode = F_OK | W_OK | R_OK | X_OK; |
72 | | constexpr int kMinimumAccessMode = std::min({F_OK, W_OK, R_OK, X_OK}); |
73 | | |
74 | | constexpr int kDefaultCopyMode = 0; |
75 | | // The copy modes can be any of UV_FS_COPYFILE_EXCL, UV_FS_COPYFILE_FICLONE or |
76 | | // UV_FS_COPYFILE_FICLONE_FORCE. They can be used in combination as well |
77 | | // (US_FS_COPYFILE_EXCL | US_FS_COPYFILE_FICLONE | |
78 | | // US_FS_COPYFILE_FICLONE_FORCE). |
79 | | constexpr int kMinimumCopyMode = std::min({kDefaultCopyMode, |
80 | | UV_FS_COPYFILE_EXCL, |
81 | | UV_FS_COPYFILE_FICLONE, |
82 | | UV_FS_COPYFILE_FICLONE_FORCE}); |
83 | | constexpr int kMaximumCopyMode = |
84 | | UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE | UV_FS_COPYFILE_FICLONE_FORCE; |
85 | | |
86 | | namespace node { |
87 | | |
88 | | using v8::ArrayBuffer; |
89 | | using v8::ArrayBufferView; |
90 | | using v8::Context; |
91 | | using v8::FunctionTemplate; |
92 | | using v8::Isolate; |
93 | | using v8::Local; |
94 | | using v8::Object; |
95 | | using v8::String; |
96 | | using v8::Template; |
97 | | using v8::Value; |
98 | | |
99 | | template <typename T> |
100 | | static void MakeUtf8String(Isolate* isolate, |
101 | | Local<Value> value, |
102 | 3.64k | MaybeStackBuffer<T>* target) { |
103 | 3.64k | Local<String> string; |
104 | 3.64k | if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&string)) return; |
105 | 3.64k | String::ValueView value_view(isolate, string); |
106 | | |
107 | 3.64k | auto value_length = value_view.length(); |
108 | | |
109 | 3.64k | if (value_view.is_one_byte()) { |
110 | 3.64k | auto const_char = reinterpret_cast<const char*>(value_view.data8()); |
111 | 3.64k | auto expected_length = |
112 | 3.64k | target->capacity() < (static_cast<size_t>(value_length) * 2 + 1) |
113 | 3.64k | ? simdutf::utf8_length_from_latin1(const_char, value_length) |
114 | 3.64k | : value_length * 2; |
115 | | |
116 | | // Add +1 for null termination. |
117 | 3.64k | target->AllocateSufficientStorage(expected_length + 1); |
118 | 3.64k | const auto actual_length = simdutf::convert_latin1_to_utf8( |
119 | 3.64k | const_char, value_length, target->out()); |
120 | 3.64k | target->SetLengthAndZeroTerminate(actual_length); |
121 | 3.64k | return; |
122 | 3.64k | } |
123 | | |
124 | | // Add +1 for null termination. |
125 | 0 | size_t storage = (3 * value_length) + 1; |
126 | 0 | target->AllocateSufficientStorage(storage); |
127 | |
|
128 | 0 | size_t length = string->WriteUtf8V2( |
129 | 0 | isolate, target->out(), storage, String::WriteFlags::kReplaceInvalidUtf8); |
130 | 0 | target->SetLengthAndZeroTerminate(length); |
131 | 0 | } |
132 | | |
133 | 3.64k | Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value) { |
134 | 3.64k | if (value.IsEmpty()) |
135 | 0 | return; |
136 | | |
137 | 3.64k | MakeUtf8String(isolate, value, this); |
138 | 3.64k | } |
139 | | |
140 | | |
141 | 0 | TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value) { |
142 | 0 | if (value.IsEmpty()) { |
143 | 0 | return; |
144 | 0 | } |
145 | | |
146 | 0 | Local<String> string; |
147 | 0 | if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&string)) return; |
148 | | |
149 | | // Allocate enough space to include the null terminator. |
150 | 0 | const size_t length = string->Length(); |
151 | 0 | AllocateSufficientStorage(length + 1); |
152 | 0 | string->WriteV2(isolate, 0, length, out()); |
153 | 0 | SetLengthAndZeroTerminate(length); |
154 | 0 | } |
155 | | |
156 | 0 | BufferValue::BufferValue(Isolate* isolate, Local<Value> value) { |
157 | | // Slightly different take on Utf8Value. If value is a String, |
158 | | // it will return a Utf8 encoded string. If value is a Buffer, |
159 | | // it will copy the data out of the Buffer as is. |
160 | 0 | if (value.IsEmpty()) { |
161 | | // Dereferencing this object will return nullptr. |
162 | 0 | Invalidate(); |
163 | 0 | return; |
164 | 0 | } |
165 | | |
166 | 0 | if (value->IsString()) { |
167 | 0 | MakeUtf8String(isolate, value, this); |
168 | 0 | } else if (value->IsArrayBufferView()) { |
169 | 0 | const size_t len = value.As<ArrayBufferView>()->ByteLength(); |
170 | | // Leave place for the terminating '\0' byte. |
171 | 0 | AllocateSufficientStorage(len + 1); |
172 | 0 | value.As<ArrayBufferView>()->CopyContents(out(), len); |
173 | 0 | SetLengthAndZeroTerminate(len); |
174 | 0 | } else { |
175 | 0 | Invalidate(); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | 0 | void LowMemoryNotification() { |
180 | 0 | if (per_process::v8_initialized) { |
181 | 0 | auto isolate = Isolate::TryGetCurrent(); |
182 | 0 | if (isolate != nullptr) { |
183 | 0 | isolate->LowMemoryNotification(); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | 35 | std::string GetProcessTitle(const char* default_title) { |
189 | 35 | std::string buf(16, '\0'); |
190 | | |
191 | 595 | for (;;) { |
192 | 595 | const int rc = uv_get_process_title(buf.data(), buf.size()); |
193 | | |
194 | 595 | if (rc == 0) |
195 | 0 | break; |
196 | | |
197 | | // If uv_setup_args() was not called, `uv_get_process_title()` will always |
198 | | // return `UV_ENOBUFS`, no matter the input size. Guard against a possible |
199 | | // infinite loop by limiting the buffer size. |
200 | 595 | if (rc != UV_ENOBUFS || buf.size() >= 1024 * 1024) |
201 | 35 | return default_title; |
202 | | |
203 | 560 | buf.resize(2 * buf.size()); |
204 | 560 | } |
205 | | |
206 | | // Strip excess trailing nul bytes. Using strlen() here is safe, |
207 | | // uv_get_process_title() always zero-terminates the result. |
208 | 0 | buf.resize(strlen(buf.data())); |
209 | |
|
210 | 0 | return buf; |
211 | 35 | } |
212 | | |
213 | 35 | std::string GetHumanReadableProcessName() { |
214 | 35 | return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid()); |
215 | 35 | } |
216 | | |
217 | 0 | void ThrowErrStringTooLong(Isolate* isolate) { |
218 | 0 | isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); |
219 | 0 | } |
220 | | |
221 | 72 | double GetCurrentTimeInMicroseconds() { |
222 | 72 | constexpr double kMicrosecondsPerSecond = 1e6; |
223 | 72 | uv_timeval64_t tv; |
224 | 72 | CHECK_EQ(0, uv_gettimeofday(&tv)); |
225 | 72 | return kMicrosecondsPerSecond * tv.tv_sec + tv.tv_usec; |
226 | 72 | } |
227 | | |
228 | 0 | int WriteFileSync(const char* path, uv_buf_t buf) { |
229 | 0 | return WriteFileSync(path, &buf, 1); |
230 | 0 | } |
231 | | |
232 | 0 | int WriteFileSync(const char* path, uv_buf_t* bufs, size_t buf_count) { |
233 | 0 | uv_fs_t req; |
234 | 0 | int fd = uv_fs_open(nullptr, |
235 | 0 | &req, |
236 | 0 | path, |
237 | 0 | O_WRONLY | O_CREAT | O_TRUNC, |
238 | 0 | S_IWUSR | S_IRUSR, |
239 | 0 | nullptr); |
240 | 0 | uv_fs_req_cleanup(&req); |
241 | 0 | if (fd < 0) { |
242 | 0 | return fd; |
243 | 0 | } |
244 | | |
245 | 0 | int err = uv_fs_write(nullptr, &req, fd, bufs, buf_count, 0, nullptr); |
246 | 0 | uv_fs_req_cleanup(&req); |
247 | 0 | if (err < 0) { |
248 | 0 | return err; |
249 | 0 | } |
250 | | |
251 | 0 | err = uv_fs_close(nullptr, &req, fd, nullptr); |
252 | 0 | uv_fs_req_cleanup(&req); |
253 | 0 | return err; |
254 | 0 | } |
255 | | |
256 | | int WriteFileSync(v8::Isolate* isolate, |
257 | | const char* path, |
258 | 0 | v8::Local<v8::String> string) { |
259 | 0 | node::Utf8Value utf8(isolate, string); |
260 | 0 | uv_buf_t buf = uv_buf_init(utf8.out(), utf8.length()); |
261 | 0 | return WriteFileSync(path, buf); |
262 | 0 | } |
263 | | |
264 | 0 | int ReadFileSync(std::string* result, const char* path) { |
265 | 0 | uv_fs_t req; |
266 | 0 | auto defer_req_cleanup = OnScopeLeave([&req]() { |
267 | 0 | uv_fs_req_cleanup(&req); |
268 | 0 | }); |
269 | |
|
270 | 0 | uv_file file = uv_fs_open(nullptr, &req, path, O_RDONLY, 0, nullptr); |
271 | 0 | if (req.result < 0) { |
272 | | // req will be cleaned up by scope leave. |
273 | 0 | return req.result; |
274 | 0 | } |
275 | 0 | uv_fs_req_cleanup(&req); |
276 | |
|
277 | 0 | auto defer_close = OnScopeLeave([file]() { |
278 | 0 | uv_fs_t close_req; |
279 | 0 | CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr)); |
280 | 0 | uv_fs_req_cleanup(&close_req); |
281 | 0 | }); |
282 | |
|
283 | 0 | *result = std::string(""); |
284 | 0 | char buffer[4096]; |
285 | 0 | uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer)); |
286 | |
|
287 | 0 | while (true) { |
288 | 0 | const int r = |
289 | 0 | uv_fs_read(nullptr, &req, file, &buf, 1, result->length(), nullptr); |
290 | 0 | if (req.result < 0) { |
291 | | // req will be cleaned up by scope leave. |
292 | 0 | return req.result; |
293 | 0 | } |
294 | 0 | uv_fs_req_cleanup(&req); |
295 | 0 | if (r <= 0) { |
296 | 0 | break; |
297 | 0 | } |
298 | 0 | result->append(buf.base, r); |
299 | 0 | } |
300 | 0 | return 0; |
301 | 0 | } |
302 | | |
303 | 0 | std::vector<char> ReadFileSync(FILE* fp) { |
304 | 0 | CHECK_EQ(ftell(fp), 0); |
305 | 0 | int err = fseek(fp, 0, SEEK_END); |
306 | 0 | CHECK_EQ(err, 0); |
307 | 0 | size_t size = ftell(fp); |
308 | 0 | CHECK_NE(size, static_cast<size_t>(-1L)); |
309 | 0 | err = fseek(fp, 0, SEEK_SET); |
310 | 0 | CHECK_EQ(err, 0); |
311 | | |
312 | 0 | std::vector<char> contents(size); |
313 | 0 | size_t num_read = fread(contents.data(), size, 1, fp); |
314 | 0 | CHECK_EQ(num_read, 1); |
315 | 0 | return contents; |
316 | 0 | } |
317 | | |
318 | 0 | void DiagnosticFilename::LocalTime(TIME_TYPE* tm_struct) { |
319 | | #ifdef _WIN32 |
320 | | GetLocalTime(tm_struct); |
321 | | #else // UNIX, macOS |
322 | 0 | struct timeval time_val; |
323 | 0 | gettimeofday(&time_val, nullptr); |
324 | 0 | localtime_r(&time_val.tv_sec, tm_struct); |
325 | 0 | #endif |
326 | 0 | } |
327 | | |
328 | | // Defined in node_internals.h |
329 | | std::string DiagnosticFilename::MakeFilename( |
330 | | uint64_t thread_id, |
331 | | const char* prefix, |
332 | 0 | const char* ext) { |
333 | 0 | std::ostringstream oss; |
334 | 0 | TIME_TYPE tm_struct; |
335 | 0 | LocalTime(&tm_struct); |
336 | 0 | oss << prefix; |
337 | | #ifdef _WIN32 |
338 | | oss << "." << std::setfill('0') << std::setw(4) << tm_struct.wYear; |
339 | | oss << std::setfill('0') << std::setw(2) << tm_struct.wMonth; |
340 | | oss << std::setfill('0') << std::setw(2) << tm_struct.wDay; |
341 | | oss << "." << std::setfill('0') << std::setw(2) << tm_struct.wHour; |
342 | | oss << std::setfill('0') << std::setw(2) << tm_struct.wMinute; |
343 | | oss << std::setfill('0') << std::setw(2) << tm_struct.wSecond; |
344 | | #else // UNIX, macOS |
345 | 0 | oss << "." |
346 | 0 | << std::setfill('0') |
347 | 0 | << std::setw(4) |
348 | 0 | << tm_struct.tm_year + 1900; |
349 | 0 | oss << std::setfill('0') |
350 | 0 | << std::setw(2) |
351 | 0 | << tm_struct.tm_mon + 1; |
352 | 0 | oss << std::setfill('0') |
353 | 0 | << std::setw(2) |
354 | 0 | << tm_struct.tm_mday; |
355 | 0 | oss << "." |
356 | 0 | << std::setfill('0') |
357 | 0 | << std::setw(2) |
358 | 0 | << tm_struct.tm_hour; |
359 | 0 | oss << std::setfill('0') |
360 | 0 | << std::setw(2) |
361 | 0 | << tm_struct.tm_min; |
362 | 0 | oss << std::setfill('0') |
363 | 0 | << std::setw(2) |
364 | 0 | << tm_struct.tm_sec; |
365 | 0 | #endif |
366 | 0 | oss << "." << uv_os_getpid(); |
367 | 0 | oss << "." << thread_id; |
368 | 0 | oss << "." << std::setfill('0') << std::setw(3) << ++seq; |
369 | 0 | oss << "." << ext; |
370 | 0 | return oss.str(); |
371 | 0 | } |
372 | | |
373 | | Local<v8::FunctionTemplate> NewFunctionTemplate( |
374 | | v8::Isolate* isolate, |
375 | | v8::FunctionCallback callback, |
376 | | Local<v8::Signature> signature, |
377 | | v8::ConstructorBehavior behavior, |
378 | | v8::SideEffectType side_effect_type, |
379 | 14.9k | const v8::CFunction* c_function) { |
380 | 14.9k | return v8::FunctionTemplate::New(isolate, |
381 | 14.9k | callback, |
382 | 14.9k | Local<v8::Value>(), |
383 | 14.9k | signature, |
384 | 14.9k | 0, |
385 | 14.9k | behavior, |
386 | 14.9k | side_effect_type, |
387 | 14.9k | c_function); |
388 | 14.9k | } |
389 | | |
390 | | void SetMethod(Local<v8::Context> context, |
391 | | Local<v8::Object> that, |
392 | | const std::string_view name, |
393 | 2.03k | v8::FunctionCallback callback) { |
394 | 2.03k | Isolate* isolate = Isolate::GetCurrent(); |
395 | 2.03k | Local<v8::Function> function = |
396 | 2.03k | NewFunctionTemplate(isolate, |
397 | 2.03k | callback, |
398 | 2.03k | Local<v8::Signature>(), |
399 | 2.03k | v8::ConstructorBehavior::kThrow, |
400 | 2.03k | v8::SideEffectType::kHasSideEffect) |
401 | 2.03k | ->GetFunction(context) |
402 | 2.03k | .ToLocalChecked(); |
403 | | // kInternalized strings are created in the old space. |
404 | 2.03k | const v8::NewStringType type = v8::NewStringType::kInternalized; |
405 | 2.03k | Local<v8::String> name_string = |
406 | 2.03k | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
407 | 2.03k | .ToLocalChecked(); |
408 | 2.03k | that->Set(context, name_string, function).Check(); |
409 | 2.03k | function->SetName(name_string); // NODE_SET_METHOD() compatibility. |
410 | 2.03k | } |
411 | | |
412 | | void SetMethod(v8::Isolate* isolate, |
413 | | v8::Local<v8::Template> that, |
414 | | const std::string_view name, |
415 | 5.21k | v8::FunctionCallback callback) { |
416 | 5.21k | Local<v8::FunctionTemplate> t = |
417 | 5.21k | NewFunctionTemplate(isolate, |
418 | 5.21k | callback, |
419 | 5.21k | Local<v8::Signature>(), |
420 | 5.21k | v8::ConstructorBehavior::kThrow, |
421 | 5.21k | v8::SideEffectType::kHasSideEffect); |
422 | | // kInternalized strings are created in the old space. |
423 | 5.21k | const v8::NewStringType type = v8::NewStringType::kInternalized; |
424 | 5.21k | Local<v8::String> name_string = |
425 | 5.21k | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
426 | 5.21k | .ToLocalChecked(); |
427 | 5.21k | that->Set(name_string, t); |
428 | 5.21k | } |
429 | | |
430 | | void SetFastMethod(Isolate* isolate, |
431 | | Local<Template> that, |
432 | | const std::string_view name, |
433 | | v8::FunctionCallback slow_callback, |
434 | 245 | const v8::CFunction* c_function) { |
435 | 245 | Local<v8::FunctionTemplate> t = |
436 | 245 | NewFunctionTemplate(isolate, |
437 | 245 | slow_callback, |
438 | 245 | Local<v8::Signature>(), |
439 | 245 | v8::ConstructorBehavior::kThrow, |
440 | 245 | v8::SideEffectType::kHasSideEffect, |
441 | 245 | c_function); |
442 | | // kInternalized strings are created in the old space. |
443 | 245 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
444 | 245 | Local<v8::String> name_string = |
445 | 245 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
446 | 245 | .ToLocalChecked(); |
447 | 245 | that->Set(name_string, t); |
448 | 245 | } |
449 | | |
450 | | void SetFastMethod(Local<v8::Context> context, |
451 | | Local<v8::Object> that, |
452 | | const std::string_view name, |
453 | | v8::FunctionCallback slow_callback, |
454 | 140 | const v8::CFunction* c_function) { |
455 | 140 | Isolate* isolate = Isolate::GetCurrent(); |
456 | 140 | Local<v8::Function> function = |
457 | 140 | NewFunctionTemplate(isolate, |
458 | 140 | slow_callback, |
459 | 140 | Local<v8::Signature>(), |
460 | 140 | v8::ConstructorBehavior::kThrow, |
461 | 140 | v8::SideEffectType::kHasSideEffect, |
462 | 140 | c_function) |
463 | 140 | ->GetFunction(context) |
464 | 140 | .ToLocalChecked(); |
465 | 140 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
466 | 140 | Local<v8::String> name_string = |
467 | 140 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
468 | 140 | .ToLocalChecked(); |
469 | 140 | that->Set(context, name_string, function).Check(); |
470 | 140 | } |
471 | | |
472 | | void SetFastMethodNoSideEffect(Local<v8::Context> context, |
473 | | Local<v8::Object> that, |
474 | | const std::string_view name, |
475 | | v8::FunctionCallback slow_callback, |
476 | 1.08k | const v8::CFunction* c_function) { |
477 | 1.08k | Isolate* isolate = Isolate::GetCurrent(); |
478 | 1.08k | Local<v8::Function> function = |
479 | 1.08k | NewFunctionTemplate(isolate, |
480 | 1.08k | slow_callback, |
481 | 1.08k | Local<v8::Signature>(), |
482 | 1.08k | v8::ConstructorBehavior::kThrow, |
483 | 1.08k | v8::SideEffectType::kHasNoSideEffect, |
484 | 1.08k | c_function) |
485 | 1.08k | ->GetFunction(context) |
486 | 1.08k | .ToLocalChecked(); |
487 | 1.08k | const v8::NewStringType type = v8::NewStringType::kInternalized; |
488 | 1.08k | Local<v8::String> name_string = |
489 | 1.08k | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
490 | 1.08k | .ToLocalChecked(); |
491 | 1.08k | that->Set(context, name_string, function).Check(); |
492 | 1.08k | } |
493 | | |
494 | | void SetFastMethodNoSideEffect(Isolate* isolate, |
495 | | Local<Template> that, |
496 | | const std::string_view name, |
497 | | v8::FunctionCallback slow_callback, |
498 | 350 | const v8::CFunction* c_function) { |
499 | 350 | Local<v8::FunctionTemplate> t = |
500 | 350 | NewFunctionTemplate(isolate, |
501 | 350 | slow_callback, |
502 | 350 | Local<v8::Signature>(), |
503 | 350 | v8::ConstructorBehavior::kThrow, |
504 | 350 | v8::SideEffectType::kHasNoSideEffect, |
505 | 350 | c_function); |
506 | | // kInternalized strings are created in the old space. |
507 | 350 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
508 | 350 | Local<v8::String> name_string = |
509 | 350 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
510 | 350 | .ToLocalChecked(); |
511 | 350 | that->Set(name_string, t); |
512 | 350 | } |
513 | | |
514 | | void SetFastMethod(Isolate* isolate, |
515 | | Local<Template> that, |
516 | | const std::string_view name, |
517 | | v8::FunctionCallback slow_callback, |
518 | 0 | const v8::MemorySpan<const v8::CFunction>& methods) { |
519 | 0 | Local<v8::FunctionTemplate> t = FunctionTemplate::NewWithCFunctionOverloads( |
520 | 0 | isolate, |
521 | 0 | slow_callback, |
522 | 0 | Local<Value>(), |
523 | 0 | Local<v8::Signature>(), |
524 | 0 | 0, |
525 | 0 | v8::ConstructorBehavior::kThrow, |
526 | 0 | v8::SideEffectType::kHasSideEffect, |
527 | 0 | methods); |
528 | | |
529 | | // kInternalized strings are created in the old space. |
530 | 0 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
531 | 0 | Local<v8::String> name_string = |
532 | 0 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
533 | 0 | .ToLocalChecked(); |
534 | 0 | that->Set(name_string, t); |
535 | 0 | } |
536 | | |
537 | | void SetFastMethodNoSideEffect( |
538 | | Isolate* isolate, |
539 | | Local<Template> that, |
540 | | const std::string_view name, |
541 | | v8::FunctionCallback slow_callback, |
542 | 35 | const v8::MemorySpan<const v8::CFunction>& methods) { |
543 | 35 | Local<v8::FunctionTemplate> t = FunctionTemplate::NewWithCFunctionOverloads( |
544 | 35 | isolate, |
545 | 35 | slow_callback, |
546 | 35 | Local<Value>(), |
547 | 35 | Local<v8::Signature>(), |
548 | 35 | 0, |
549 | 35 | v8::ConstructorBehavior::kThrow, |
550 | 35 | v8::SideEffectType::kHasNoSideEffect, |
551 | 35 | methods); |
552 | | |
553 | | // kInternalized strings are created in the old space. |
554 | 35 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
555 | 35 | Local<v8::String> name_string = |
556 | 35 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
557 | 35 | .ToLocalChecked(); |
558 | 35 | that->Set(name_string, t); |
559 | 35 | } |
560 | | |
561 | | void SetMethodNoSideEffect(Local<v8::Context> context, |
562 | | Local<v8::Object> that, |
563 | | const std::string_view name, |
564 | 1.36k | v8::FunctionCallback callback) { |
565 | 1.36k | Isolate* isolate = Isolate::GetCurrent(); |
566 | 1.36k | Local<v8::Function> function = |
567 | 1.36k | NewFunctionTemplate(isolate, |
568 | 1.36k | callback, |
569 | 1.36k | Local<v8::Signature>(), |
570 | 1.36k | v8::ConstructorBehavior::kThrow, |
571 | 1.36k | v8::SideEffectType::kHasNoSideEffect) |
572 | 1.36k | ->GetFunction(context) |
573 | 1.36k | .ToLocalChecked(); |
574 | | // kInternalized strings are created in the old space. |
575 | 1.36k | const v8::NewStringType type = v8::NewStringType::kInternalized; |
576 | 1.36k | Local<v8::String> name_string = |
577 | 1.36k | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
578 | 1.36k | .ToLocalChecked(); |
579 | 1.36k | that->Set(context, name_string, function).Check(); |
580 | 1.36k | function->SetName(name_string); // NODE_SET_METHOD() compatibility. |
581 | 1.36k | } |
582 | | |
583 | | void SetMethodNoSideEffect(Isolate* isolate, |
584 | | Local<v8::Template> that, |
585 | | const std::string_view name, |
586 | 385 | v8::FunctionCallback callback) { |
587 | 385 | Local<v8::FunctionTemplate> t = |
588 | 385 | NewFunctionTemplate(isolate, |
589 | 385 | callback, |
590 | 385 | Local<v8::Signature>(), |
591 | 385 | v8::ConstructorBehavior::kThrow, |
592 | 385 | v8::SideEffectType::kHasNoSideEffect); |
593 | | // kInternalized strings are created in the old space. |
594 | 385 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
595 | 385 | Local<v8::String> name_string = |
596 | 385 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
597 | 385 | .ToLocalChecked(); |
598 | 385 | that->Set(name_string, t); |
599 | 385 | } |
600 | | |
601 | | void SetProtoDispose(v8::Isolate* isolate, |
602 | | v8::Local<v8::FunctionTemplate> that, |
603 | 0 | v8::FunctionCallback callback) { |
604 | 0 | Local<v8::Signature> signature = v8::Signature::New(isolate, that); |
605 | 0 | Local<v8::FunctionTemplate> t = |
606 | 0 | NewFunctionTemplate(isolate, |
607 | 0 | callback, |
608 | 0 | signature, |
609 | 0 | v8::ConstructorBehavior::kThrow, |
610 | 0 | v8::SideEffectType::kHasSideEffect); |
611 | 0 | that->PrototypeTemplate()->Set(v8::Symbol::GetDispose(isolate), t); |
612 | 0 | } |
613 | | |
614 | | void SetProtoAsyncDispose(v8::Isolate* isolate, |
615 | | v8::Local<v8::FunctionTemplate> that, |
616 | 0 | v8::FunctionCallback callback) { |
617 | 0 | Local<v8::Signature> signature = v8::Signature::New(isolate, that); |
618 | 0 | Local<v8::FunctionTemplate> t = |
619 | 0 | NewFunctionTemplate(isolate, |
620 | 0 | callback, |
621 | 0 | signature, |
622 | 0 | v8::ConstructorBehavior::kThrow, |
623 | 0 | v8::SideEffectType::kHasSideEffect); |
624 | 0 | that->PrototypeTemplate()->Set(v8::Symbol::GetAsyncDispose(isolate), t); |
625 | 0 | } |
626 | | |
627 | | void SetProtoMethod(v8::Isolate* isolate, |
628 | | Local<v8::FunctionTemplate> that, |
629 | | const std::string_view name, |
630 | 2.45k | v8::FunctionCallback callback) { |
631 | 2.45k | Local<v8::Signature> signature = v8::Signature::New(isolate, that); |
632 | 2.45k | Local<v8::FunctionTemplate> t = |
633 | 2.45k | NewFunctionTemplate(isolate, |
634 | 2.45k | callback, |
635 | 2.45k | signature, |
636 | 2.45k | v8::ConstructorBehavior::kThrow, |
637 | 2.45k | v8::SideEffectType::kHasSideEffect); |
638 | | // kInternalized strings are created in the old space. |
639 | 2.45k | const v8::NewStringType type = v8::NewStringType::kInternalized; |
640 | 2.45k | Local<v8::String> name_string = |
641 | 2.45k | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
642 | 2.45k | .ToLocalChecked(); |
643 | 2.45k | that->PrototypeTemplate()->Set(name_string, t); |
644 | 2.45k | t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. |
645 | 2.45k | } |
646 | | |
647 | | void SetProtoMethodNoSideEffect(v8::Isolate* isolate, |
648 | | Local<v8::FunctionTemplate> that, |
649 | | const std::string_view name, |
650 | 490 | v8::FunctionCallback callback) { |
651 | 490 | Local<v8::Signature> signature = v8::Signature::New(isolate, that); |
652 | 490 | Local<v8::FunctionTemplate> t = |
653 | 490 | NewFunctionTemplate(isolate, |
654 | 490 | callback, |
655 | 490 | signature, |
656 | 490 | v8::ConstructorBehavior::kThrow, |
657 | 490 | v8::SideEffectType::kHasNoSideEffect); |
658 | | // kInternalized strings are created in the old space. |
659 | 490 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
660 | 490 | Local<v8::String> name_string = |
661 | 490 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
662 | 490 | .ToLocalChecked(); |
663 | 490 | that->PrototypeTemplate()->Set(name_string, t); |
664 | 490 | t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. |
665 | 490 | } |
666 | | |
667 | | void SetInstanceMethod(v8::Isolate* isolate, |
668 | | Local<v8::FunctionTemplate> that, |
669 | | const std::string_view name, |
670 | 0 | v8::FunctionCallback callback) { |
671 | 0 | Local<v8::Signature> signature = v8::Signature::New(isolate, that); |
672 | 0 | Local<v8::FunctionTemplate> t = |
673 | 0 | NewFunctionTemplate(isolate, |
674 | 0 | callback, |
675 | 0 | signature, |
676 | 0 | v8::ConstructorBehavior::kThrow, |
677 | 0 | v8::SideEffectType::kHasSideEffect); |
678 | | // kInternalized strings are created in the old space. |
679 | 0 | const v8::NewStringType type = v8::NewStringType::kInternalized; |
680 | 0 | Local<v8::String> name_string = |
681 | 0 | v8::String::NewFromUtf8(isolate, name.data(), type, name.size()) |
682 | 0 | .ToLocalChecked(); |
683 | 0 | that->InstanceTemplate()->Set(name_string, t); |
684 | 0 | t->SetClassName(name_string); |
685 | 0 | } |
686 | | |
687 | | void SetConstructorFunction(Local<v8::Context> context, |
688 | | Local<v8::Object> that, |
689 | | const char* name, |
690 | | Local<v8::FunctionTemplate> tmpl, |
691 | 70 | SetConstructorFunctionFlag flag) { |
692 | 70 | Isolate* isolate = Isolate::GetCurrent(); |
693 | 70 | SetConstructorFunction( |
694 | 70 | context, that, OneByteString(isolate, name), tmpl, flag); |
695 | 70 | } |
696 | | |
697 | | void SetConstructorFunction(Local<Context> context, |
698 | | Local<Object> that, |
699 | | Local<String> name, |
700 | | Local<FunctionTemplate> tmpl, |
701 | 140 | SetConstructorFunctionFlag flag) { |
702 | 140 | if (flag == SetConstructorFunctionFlag::SET_CLASS_NAME) [[likely]] { |
703 | 140 | tmpl->SetClassName(name); |
704 | 140 | } |
705 | 140 | that->Set(context, name, tmpl->GetFunction(context).ToLocalChecked()).Check(); |
706 | 140 | } |
707 | | |
708 | | void SetConstructorFunction(Isolate* isolate, |
709 | | Local<Template> that, |
710 | | const char* name, |
711 | | Local<FunctionTemplate> tmpl, |
712 | 350 | SetConstructorFunctionFlag flag) { |
713 | 350 | SetConstructorFunction( |
714 | 350 | isolate, that, OneByteString(isolate, name), tmpl, flag); |
715 | 350 | } |
716 | | |
717 | | void SetConstructorFunction(Isolate* isolate, |
718 | | Local<Template> that, |
719 | | Local<String> name, |
720 | | Local<FunctionTemplate> tmpl, |
721 | 385 | SetConstructorFunctionFlag flag) { |
722 | 385 | if (flag == SetConstructorFunctionFlag::SET_CLASS_NAME) [[likely]] { |
723 | 350 | tmpl->SetClassName(name); |
724 | 350 | } |
725 | 385 | that->Set(name, tmpl); |
726 | 385 | } |
727 | | |
728 | 2.62k | Local<String> UnionBytes::ToStringChecked(Isolate* isolate) const { |
729 | 2.62k | if (is_one_byte()) { |
730 | 2.55k | return String::NewExternalOneByte(isolate, one_byte_resource_) |
731 | 2.55k | .ToLocalChecked(); |
732 | 2.55k | } else { |
733 | 70 | return String::NewExternalTwoByte(isolate, two_byte_resource_) |
734 | 70 | .ToLocalChecked(); |
735 | 70 | } |
736 | 2.62k | } |
737 | | |
738 | | RAIIIsolateWithoutEntering::RAIIIsolateWithoutEntering(const SnapshotData* data) |
739 | 0 | : allocator_{ArrayBuffer::Allocator::NewDefaultAllocator()} { |
740 | 0 | isolate_ = Isolate::Allocate(); |
741 | 0 | CHECK_NOT_NULL(isolate_); |
742 | 0 | per_process::v8_platform.Platform()->RegisterIsolate(isolate_, |
743 | 0 | uv_default_loop()); |
744 | 0 | Isolate::CreateParams params; |
745 | 0 | if (data != nullptr) { |
746 | 0 | SnapshotBuilder::InitializeIsolateParams(data, ¶ms); |
747 | 0 | } |
748 | 0 | params.array_buffer_allocator = allocator_.get(); |
749 | 0 | params.cpp_heap = v8::CppHeap::Create(per_process::v8_platform.Platform(), |
750 | 0 | v8::CppHeapCreateParams{{}}) |
751 | 0 | .release(); |
752 | 0 | Isolate::Initialize(isolate_, params); |
753 | 0 | } |
754 | | |
755 | 0 | RAIIIsolateWithoutEntering::~RAIIIsolateWithoutEntering() { |
756 | 0 | per_process::v8_platform.Platform()->DisposeIsolate(isolate_); |
757 | 0 | } |
758 | | |
759 | | RAIIIsolate::RAIIIsolate(const SnapshotData* data) |
760 | 0 | : isolate_{data}, isolate_scope_{isolate_.get()} {} |
761 | | |
762 | 0 | RAIIIsolate::~RAIIIsolate() {} |
763 | | |
764 | | // Returns a string representation of the input value, including type. |
765 | | // JavaScript implementation is available in lib/internal/errors.js |
766 | | std::string DetermineSpecificErrorType(Environment* env, |
767 | 0 | v8::Local<v8::Value> input) { |
768 | 0 | if (input->IsFunction()) { |
769 | 0 | return "function"; |
770 | 0 | } else if (input->IsString()) { |
771 | 0 | auto value = Utf8Value(env->isolate(), input).ToString(); |
772 | 0 | if (value.size() > 28) { |
773 | 0 | value = value.substr(0, 25) + "..."; |
774 | 0 | } |
775 | 0 | if (value.find('\'') == std::string::npos) { |
776 | 0 | return SPrintF("type string ('%s')", value); |
777 | 0 | } |
778 | | |
779 | | // Stringify the input value. |
780 | 0 | Local<String> stringified = |
781 | 0 | v8::JSON::Stringify(env->context(), input).ToLocalChecked(); |
782 | 0 | Utf8Value stringified_value(env->isolate(), stringified); |
783 | 0 | return SPrintF("type string (%s)", stringified_value.out()); |
784 | 0 | } else if (input->IsObject()) { |
785 | 0 | v8::Local<v8::String> constructor_name = |
786 | 0 | input.As<v8::Object>()->GetConstructorName(); |
787 | 0 | Utf8Value name(env->isolate(), constructor_name); |
788 | 0 | return SPrintF("an instance of %s", name.out()); |
789 | 0 | } else if (input->IsSymbol()) { |
790 | 0 | v8::MaybeLocal<v8::String> str = |
791 | 0 | input.As<v8::Symbol>()->ToDetailString(env->context()); |
792 | 0 | v8::Local<v8::String> js_str; |
793 | 0 | if (!str.ToLocal(&js_str)) { |
794 | 0 | return "Symbol"; |
795 | 0 | } |
796 | 0 | Utf8Value name(env->isolate(), js_str); |
797 | | // Symbol(xxx) |
798 | 0 | return name.out(); |
799 | 0 | } |
800 | | |
801 | 0 | Utf8Value utf8_value(env->isolate(), |
802 | 0 | input->ToString(env->context()).ToLocalChecked()); |
803 | |
|
804 | 0 | if (input->IsNumber() || input->IsInt32() || input->IsUint32()) { |
805 | 0 | auto value = input.As<v8::Number>()->Value(); |
806 | 0 | if (std::isnan(value)) { |
807 | 0 | return "type number (NaN)"; |
808 | 0 | } else if (std::isinf(value)) { |
809 | 0 | return "type number (Infinity)"; |
810 | 0 | } |
811 | 0 | return SPrintF("type number (%s)", utf8_value.out()); |
812 | 0 | } else if (input->IsBigInt() || input->IsBoolean() || input->IsSymbol()) { |
813 | 0 | Utf8Value type(env->isolate(), input->TypeOf(env->isolate())); |
814 | 0 | return SPrintF("type %s (%s)", type.out(), utf8_value.out()); |
815 | 0 | } |
816 | | |
817 | | // For example: null, undefined |
818 | 0 | return utf8_value.ToString(); |
819 | 0 | } |
820 | | |
821 | | v8::Maybe<int32_t> GetValidatedFd(Environment* env, |
822 | 0 | v8::Local<v8::Value> input) { |
823 | 0 | if (!input->IsInt32() && !input->IsNumber()) { |
824 | 0 | std::string error_type = node::DetermineSpecificErrorType(env, input); |
825 | 0 | THROW_ERR_INVALID_ARG_TYPE(env, |
826 | 0 | "The \"fd\" argument must be of type " |
827 | 0 | "number. Received %s", |
828 | 0 | error_type.c_str()); |
829 | 0 | return v8::Nothing<int32_t>(); |
830 | 0 | } |
831 | | |
832 | 0 | const double fd = input.As<v8::Number>()->Value(); |
833 | 0 | const bool is_out_of_range = fd < 0 || fd > INT32_MAX; |
834 | |
|
835 | 0 | if (is_out_of_range || !IsSafeJsInt(input)) { |
836 | 0 | Local<String> str; |
837 | 0 | if (!input->ToDetailString(env->context()).ToLocal(&str)) { |
838 | 0 | return v8::Nothing<int32_t>(); |
839 | 0 | } |
840 | 0 | Utf8Value utf8_value(env->isolate(), str); |
841 | 0 | if (is_out_of_range && !std::isinf(fd)) { |
842 | 0 | THROW_ERR_OUT_OF_RANGE(env, |
843 | 0 | "The value of \"fd\" is out of range. " |
844 | 0 | "It must be >= 0 && <= %s. Received %s", |
845 | 0 | std::to_string(INT32_MAX), |
846 | 0 | utf8_value.out()); |
847 | 0 | } else { |
848 | 0 | THROW_ERR_OUT_OF_RANGE( |
849 | 0 | env, |
850 | 0 | "The value of \"fd\" is out of range. It must be an integer. " |
851 | 0 | "Received %s", |
852 | 0 | utf8_value.out()); |
853 | 0 | } |
854 | 0 | return v8::Nothing<int32_t>(); |
855 | 0 | } |
856 | | |
857 | 0 | return v8::Just(static_cast<int32_t>(fd)); |
858 | 0 | } |
859 | | |
860 | | v8::Maybe<int> GetValidFileMode(Environment* env, |
861 | | v8::Local<v8::Value> input, |
862 | 0 | uv_fs_type type) { |
863 | | // Allow only int32 or null/undefined values. |
864 | 0 | if (input->IsNumber()) { |
865 | | // We cast the input to v8::Number to avoid overflows. |
866 | 0 | auto num = input.As<v8::Number>()->Value(); |
867 | | |
868 | | // Handle infinity and NaN values |
869 | 0 | if (std::isinf(num) || std::isnan(num)) { |
870 | 0 | THROW_ERR_OUT_OF_RANGE(env, "mode is out of range"); |
871 | 0 | return v8::Nothing<int>(); |
872 | 0 | } |
873 | 0 | } else if (!input->IsNullOrUndefined()) { |
874 | 0 | THROW_ERR_INVALID_ARG_TYPE(env, "mode must be int32 or null/undefined"); |
875 | 0 | return v8::Nothing<int>(); |
876 | 0 | } |
877 | | |
878 | 0 | int min = kMinimumAccessMode; |
879 | 0 | int max = kMaximumAccessMode; |
880 | 0 | int def = F_OK; |
881 | |
|
882 | 0 | CHECK(type == UV_FS_ACCESS || type == UV_FS_COPYFILE); |
883 | | |
884 | 0 | if (type == UV_FS_COPYFILE) { |
885 | 0 | min = kMinimumCopyMode; |
886 | 0 | max = kMaximumCopyMode; |
887 | 0 | def = input->IsNullOrUndefined() ? kDefaultCopyMode |
888 | 0 | : input.As<v8::Int32>()->Value(); |
889 | 0 | } |
890 | |
|
891 | 0 | if (input->IsNullOrUndefined()) { |
892 | 0 | return v8::Just(def); |
893 | 0 | } |
894 | | |
895 | 0 | const int mode = input.As<v8::Int32>()->Value(); |
896 | 0 | if (mode < min || mode > max) { |
897 | 0 | THROW_ERR_OUT_OF_RANGE( |
898 | 0 | env, "mode is out of range: >= %d && <= %d", min, max); |
899 | 0 | return v8::Nothing<int>(); |
900 | 0 | } |
901 | | |
902 | 0 | return v8::Just(mode); |
903 | 0 | } |
904 | | |
905 | | } // namespace node |