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 "util-inl.h"
24
25 #include "debug_utils-inl.h"
26 #include "env-inl.h"
27 #include "node_buffer.h"
28 #include "node_errors.h"
29 #include "node_internals.h"
30 #include "node_util.h"
31 #include "string_bytes.h"
32 #include "uv.h"
33 #include "init_param.h"
34
35 #ifdef _WIN32
36 #include <io.h> // _S_IREAD _S_IWRITE
37 #include <time.h>
38 #ifndef S_IRUSR
39 #define S_IRUSR _S_IREAD
40 #endif // S_IRUSR
41 #ifndef S_IWUSR
42 #define S_IWUSR _S_IWRITE
43 #endif // S_IWUSR
44 #else
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #endif
48
49 #include <atomic>
50 #include <cstdio>
51 #include <cstring>
52 #include <iomanip>
53 #include <sstream>
54
55 #define ARGBUFFSIZE 32
56
57 static std::atomic_int seq = {0}; // Sequence number for diagnostic filenames.
58
59 namespace node {
60
61 using v8::ArrayBufferView;
62 using v8::Isolate;
63 using v8::Local;
64 using v8::String;
65 using v8::Value;
66
ReadSystemXpmState()67 bool ReadSystemXpmState() {
68 char buffer[ARGBUFFSIZE] = { 0 };
69 uint32_t buffSize = sizeof(buffer);
70
71 if (SystemGetParameter("ohos.boot.advsecmode.state", buffer, &buffSize) == 0 && strcmp(buffer, "0") != 0) {
72 return true;
73 }
74 return false;
75 }
76
77 template <typename T>
MakeUtf8String(Isolate* isolate, Local<Value> value, MaybeStackBuffer<T>* target)78 static void MakeUtf8String(Isolate* isolate,
79 Local<Value> value,
80 MaybeStackBuffer<T>* target) {
81 Local<String> string;
82 if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&string)) return;
83
84 size_t storage;
85 if (!StringBytes::StorageSize(isolate, string, UTF8).To(&storage)) return;
86 storage += 1;
87 target->AllocateSufficientStorage(storage);
88 const int flags =
89 String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
90 const int length =
91 string->WriteUtf8(isolate, target->out(), storage, nullptr, flags);
92 target->SetLengthAndZeroTerminate(length);
93 }
94
Utf8Value(Isolate* isolate, Local<Value> value)95 Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value) {
96 if (value.IsEmpty())
97 return;
98
99 MakeUtf8String(isolate, value, this);
100 }
101
102
TwoByteValue(Isolate* isolate, Local<Value> value)103 TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value) {
104 if (value.IsEmpty()) {
105 return;
106 }
107
108 Local<String> string;
109 if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&string)) return;
110
111 // Allocate enough space to include the null terminator
112 const size_t storage = string->Length() + 1;
113 AllocateSufficientStorage(storage);
114
115 const int flags = String::NO_NULL_TERMINATION;
116 const int length = string->Write(isolate, out(), 0, storage, flags);
117 SetLengthAndZeroTerminate(length);
118 }
119
BufferValue(Isolate* isolate, Local<Value> value)120 BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
121 // Slightly different take on Utf8Value. If value is a String,
122 // it will return a Utf8 encoded string. If value is a Buffer,
123 // it will copy the data out of the Buffer as is.
124 if (value.IsEmpty()) {
125 // Dereferencing this object will return nullptr.
126 Invalidate();
127 return;
128 }
129
130 if (value->IsString()) {
131 MakeUtf8String(isolate, value, this);
132 } else if (value->IsArrayBufferView()) {
133 const size_t len = value.As<ArrayBufferView>()->ByteLength();
134 // Leave place for the terminating '\0' byte.
135 AllocateSufficientStorage(len + 1);
136 value.As<ArrayBufferView>()->CopyContents(out(), len);
137 SetLengthAndZeroTerminate(len);
138 } else {
139 Invalidate();
140 }
141 }
142
LowMemoryNotification()143 void LowMemoryNotification() {
144 if (per_process::v8_initialized) {
145 auto isolate = Isolate::TryGetCurrent();
146 if (isolate != nullptr) {
147 isolate->LowMemoryNotification();
148 }
149 }
150 }
151
GetProcessTitle(const char* default_title)152 std::string GetProcessTitle(const char* default_title) {
153 std::string buf(16, '\0');
154
155 for (;;) {
156 const int rc = uv_get_process_title(buf.data(), buf.size());
157
158 if (rc == 0)
159 break;
160
161 // If uv_setup_args() was not called, `uv_get_process_title()` will always
162 // return `UV_ENOBUFS`, no matter the input size. Guard against a possible
163 // infinite loop by limiting the buffer size.
164 if (rc != UV_ENOBUFS || buf.size() >= 1024 * 1024)
165 return default_title;
166
167 buf.resize(2 * buf.size());
168 }
169
170 // Strip excess trailing nul bytes. Using strlen() here is safe,
171 // uv_get_process_title() always zero-terminates the result.
172 buf.resize(strlen(buf.data()));
173
174 return buf;
175 }
176
GetHumanReadableProcessName()177 std::string GetHumanReadableProcessName() {
178 return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid());
179 }
180
SplitString(const std::string_view in, const std::string_view delim)181 std::vector<std::string_view> SplitString(const std::string_view in,
182 const std::string_view delim) {
183 std::vector<std::string_view> out;
184
185 for (auto first = in.data(), second = in.data(), last = first + in.size();
186 second != last && first != last;
187 first = second + 1) {
188 second =
189 std::find_first_of(first, last, std::cbegin(delim), std::cend(delim));
190
191 if (first != second) {
192 out.emplace_back(first, second - first);
193 }
194 }
195
196 return out;
197 }
198
ThrowErrStringTooLong(Isolate* isolate)199 void ThrowErrStringTooLong(Isolate* isolate) {
200 isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
201 }
202
GetCurrentTimeInMicroseconds()203 double GetCurrentTimeInMicroseconds() {
204 constexpr double kMicrosecondsPerSecond = 1e6;
205 uv_timeval64_t tv;
206 CHECK_EQ(0, uv_gettimeofday(&tv));
207 return kMicrosecondsPerSecond * tv.tv_sec + tv.tv_usec;
208 }
209
WriteFileSync(const char* path, uv_buf_t buf)210 int WriteFileSync(const char* path, uv_buf_t buf) {
211 uv_fs_t req;
212 int fd = uv_fs_open(nullptr,
213 &req,
214 path,
215 O_WRONLY | O_CREAT | O_TRUNC,
216 S_IWUSR | S_IRUSR,
217 nullptr);
218 uv_fs_req_cleanup(&req);
219 if (fd < 0) {
220 return fd;
221 }
222
223 int err = uv_fs_write(nullptr, &req, fd, &buf, 1, 0, nullptr);
224 uv_fs_req_cleanup(&req);
225 if (err < 0) {
226 return err;
227 }
228
229 err = uv_fs_close(nullptr, &req, fd, nullptr);
230 uv_fs_req_cleanup(&req);
231 return err;
232 }
233
WriteFileSync(v8::Isolate* isolate, const char* path, v8::Local<v8::String> string)234 int WriteFileSync(v8::Isolate* isolate,
235 const char* path,
236 v8::Local<v8::String> string) {
237 node::Utf8Value utf8(isolate, string);
238 uv_buf_t buf = uv_buf_init(utf8.out(), utf8.length());
239 return WriteFileSync(path, buf);
240 }
241
ReadFileSync(std::string* result, const char* path)242 int ReadFileSync(std::string* result, const char* path) {
243 uv_fs_t req;
244 auto defer_req_cleanup = OnScopeLeave([&req]() {
245 uv_fs_req_cleanup(&req);
246 });
247
248 uv_file file = uv_fs_open(nullptr, &req, path, O_RDONLY, 0, nullptr);
249 if (req.result < 0) {
250 // req will be cleaned up by scope leave.
251 return req.result;
252 }
253 uv_fs_req_cleanup(&req);
254
255 auto defer_close = OnScopeLeave([file]() {
256 uv_fs_t close_req;
257 CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr));
258 uv_fs_req_cleanup(&close_req);
259 });
260
261 *result = std::string("");
262 char buffer[4096];
263 uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
264
265 while (true) {
266 const int r =
267 uv_fs_read(nullptr, &req, file, &buf, 1, result->length(), nullptr);
268 if (req.result < 0) {
269 // req will be cleaned up by scope leave.
270 return req.result;
271 }
272 uv_fs_req_cleanup(&req);
273 if (r <= 0) {
274 break;
275 }
276 result->append(buf.base, r);
277 }
278 return 0;
279 }
280
LocalTime(TIME_TYPE* tm_struct)281 void DiagnosticFilename::LocalTime(TIME_TYPE* tm_struct) {
282 #ifdef _WIN32
283 GetLocalTime(tm_struct);
284 #else // UNIX, OSX
285 struct timeval time_val;
286 gettimeofday(&time_val, nullptr);
287 localtime_r(&time_val.tv_sec, tm_struct);
288 #endif
289 }
290
291 // Defined in node_internals.h
MakeFilename( uint64_t thread_id, const char* prefix, const char* ext)292 std::string DiagnosticFilename::MakeFilename(
293 uint64_t thread_id,
294 const char* prefix,
295 const char* ext) {
296 std::ostringstream oss;
297 TIME_TYPE tm_struct;
298 LocalTime(&tm_struct);
299 oss << prefix;
300 #ifdef _WIN32
301 oss << "." << std::setfill('0') << std::setw(4) << tm_struct.wYear;
302 oss << std::setfill('0') << std::setw(2) << tm_struct.wMonth;
303 oss << std::setfill('0') << std::setw(2) << tm_struct.wDay;
304 oss << "." << std::setfill('0') << std::setw(2) << tm_struct.wHour;
305 oss << std::setfill('0') << std::setw(2) << tm_struct.wMinute;
306 oss << std::setfill('0') << std::setw(2) << tm_struct.wSecond;
307 #else // UNIX, OSX
308 oss << "."
309 << std::setfill('0')
310 << std::setw(4)
311 << tm_struct.tm_year + 1900;
312 oss << std::setfill('0')
313 << std::setw(2)
314 << tm_struct.tm_mon + 1;
315 oss << std::setfill('0')
316 << std::setw(2)
317 << tm_struct.tm_mday;
318 oss << "."
319 << std::setfill('0')
320 << std::setw(2)
321 << tm_struct.tm_hour;
322 oss << std::setfill('0')
323 << std::setw(2)
324 << tm_struct.tm_min;
325 oss << std::setfill('0')
326 << std::setw(2)
327 << tm_struct.tm_sec;
328 #endif
329 oss << "." << uv_os_getpid();
330 oss << "." << thread_id;
331 oss << "." << std::setfill('0') << std::setw(3) << ++seq;
332 oss << "." << ext;
333 return oss.str();
334 }
335
NewFunctionTemplate( v8::Isolate* isolate, v8::FunctionCallback callback, Local<v8::Signature> signature, v8::ConstructorBehavior behavior, v8::SideEffectType side_effect_type, const v8::CFunction* c_function)336 Local<v8::FunctionTemplate> NewFunctionTemplate(
337 v8::Isolate* isolate,
338 v8::FunctionCallback callback,
339 Local<v8::Signature> signature,
340 v8::ConstructorBehavior behavior,
341 v8::SideEffectType side_effect_type,
342 const v8::CFunction* c_function) {
343 return v8::FunctionTemplate::New(isolate,
344 callback,
345 Local<v8::Value>(),
346 signature,
347 0,
348 behavior,
349 side_effect_type,
350 c_function);
351 }
352
SetMethod(Local<v8::Context> context, Local<v8::Object> that, const char* name, v8::FunctionCallback callback)353 void SetMethod(Local<v8::Context> context,
354 Local<v8::Object> that,
355 const char* name,
356 v8::FunctionCallback callback) {
357 Isolate* isolate = context->GetIsolate();
358 Local<v8::Function> function =
359 NewFunctionTemplate(isolate,
360 callback,
361 Local<v8::Signature>(),
362 v8::ConstructorBehavior::kThrow,
363 v8::SideEffectType::kHasSideEffect)
364 ->GetFunction(context)
365 .ToLocalChecked();
366 // kInternalized strings are created in the old space.
367 const v8::NewStringType type = v8::NewStringType::kInternalized;
368 Local<v8::String> name_string =
369 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
370 that->Set(context, name_string, function).Check();
371 function->SetName(name_string); // NODE_SET_METHOD() compatibility.
372 }
373
SetMethod(v8::Isolate* isolate, v8::Local<v8::Template> that, const char* name, v8::FunctionCallback callback)374 void SetMethod(v8::Isolate* isolate,
375 v8::Local<v8::Template> that,
376 const char* name,
377 v8::FunctionCallback callback) {
378 Local<v8::FunctionTemplate> t =
379 NewFunctionTemplate(isolate,
380 callback,
381 Local<v8::Signature>(),
382 v8::ConstructorBehavior::kThrow,
383 v8::SideEffectType::kHasSideEffect);
384 // kInternalized strings are created in the old space.
385 const v8::NewStringType type = v8::NewStringType::kInternalized;
386 Local<v8::String> name_string =
387 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
388 that->Set(name_string, t);
389 }
390
SetFastMethod(Local<v8::Context> context, Local<v8::Object> that, const char* name, v8::FunctionCallback slow_callback, const v8::CFunction* c_function)391 void SetFastMethod(Local<v8::Context> context,
392 Local<v8::Object> that,
393 const char* name,
394 v8::FunctionCallback slow_callback,
395 const v8::CFunction* c_function) {
396 Isolate* isolate = context->GetIsolate();
397 Local<v8::Function> function =
398 NewFunctionTemplate(isolate,
399 slow_callback,
400 Local<v8::Signature>(),
401 v8::ConstructorBehavior::kThrow,
402 v8::SideEffectType::kHasSideEffect,
403 c_function)
404 ->GetFunction(context)
405 .ToLocalChecked();
406 const v8::NewStringType type = v8::NewStringType::kInternalized;
407 Local<v8::String> name_string =
408 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
409 that->Set(context, name_string, function).Check();
410 }
411
SetFastMethodNoSideEffect(Local<v8::Context> context, Local<v8::Object> that, const char* name, v8::FunctionCallback slow_callback, const v8::CFunction* c_function)412 void SetFastMethodNoSideEffect(Local<v8::Context> context,
413 Local<v8::Object> that,
414 const char* name,
415 v8::FunctionCallback slow_callback,
416 const v8::CFunction* c_function) {
417 Isolate* isolate = context->GetIsolate();
418 Local<v8::Function> function =
419 NewFunctionTemplate(isolate,
420 slow_callback,
421 Local<v8::Signature>(),
422 v8::ConstructorBehavior::kThrow,
423 v8::SideEffectType::kHasNoSideEffect,
424 c_function)
425 ->GetFunction(context)
426 .ToLocalChecked();
427 const v8::NewStringType type = v8::NewStringType::kInternalized;
428 Local<v8::String> name_string =
429 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
430 that->Set(context, name_string, function).Check();
431 }
432
SetMethodNoSideEffect(Local<v8::Context> context, Local<v8::Object> that, const char* name, v8::FunctionCallback callback)433 void SetMethodNoSideEffect(Local<v8::Context> context,
434 Local<v8::Object> that,
435 const char* name,
436 v8::FunctionCallback callback) {
437 Isolate* isolate = context->GetIsolate();
438 Local<v8::Function> function =
439 NewFunctionTemplate(isolate,
440 callback,
441 Local<v8::Signature>(),
442 v8::ConstructorBehavior::kThrow,
443 v8::SideEffectType::kHasNoSideEffect)
444 ->GetFunction(context)
445 .ToLocalChecked();
446 // kInternalized strings are created in the old space.
447 const v8::NewStringType type = v8::NewStringType::kInternalized;
448 Local<v8::String> name_string =
449 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
450 that->Set(context, name_string, function).Check();
451 function->SetName(name_string); // NODE_SET_METHOD() compatibility.
452 }
453
SetProtoMethod(v8::Isolate* isolate, Local<v8::FunctionTemplate> that, const char* name, v8::FunctionCallback callback)454 void SetProtoMethod(v8::Isolate* isolate,
455 Local<v8::FunctionTemplate> that,
456 const char* name,
457 v8::FunctionCallback callback) {
458 Local<v8::Signature> signature = v8::Signature::New(isolate, that);
459 Local<v8::FunctionTemplate> t =
460 NewFunctionTemplate(isolate,
461 callback,
462 signature,
463 v8::ConstructorBehavior::kThrow,
464 v8::SideEffectType::kHasSideEffect);
465 // kInternalized strings are created in the old space.
466 const v8::NewStringType type = v8::NewStringType::kInternalized;
467 Local<v8::String> name_string =
468 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
469 that->PrototypeTemplate()->Set(name_string, t);
470 t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility.
471 }
472
SetProtoMethodNoSideEffect(v8::Isolate* isolate, Local<v8::FunctionTemplate> that, const char* name, v8::FunctionCallback callback)473 void SetProtoMethodNoSideEffect(v8::Isolate* isolate,
474 Local<v8::FunctionTemplate> that,
475 const char* name,
476 v8::FunctionCallback callback) {
477 Local<v8::Signature> signature = v8::Signature::New(isolate, that);
478 Local<v8::FunctionTemplate> t =
479 NewFunctionTemplate(isolate,
480 callback,
481 signature,
482 v8::ConstructorBehavior::kThrow,
483 v8::SideEffectType::kHasNoSideEffect);
484 // kInternalized strings are created in the old space.
485 const v8::NewStringType type = v8::NewStringType::kInternalized;
486 Local<v8::String> name_string =
487 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
488 that->PrototypeTemplate()->Set(name_string, t);
489 t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility.
490 }
491
SetInstanceMethod(v8::Isolate* isolate, Local<v8::FunctionTemplate> that, const char* name, v8::FunctionCallback callback)492 void SetInstanceMethod(v8::Isolate* isolate,
493 Local<v8::FunctionTemplate> that,
494 const char* name,
495 v8::FunctionCallback callback) {
496 Local<v8::Signature> signature = v8::Signature::New(isolate, that);
497 Local<v8::FunctionTemplate> t =
498 NewFunctionTemplate(isolate,
499 callback,
500 signature,
501 v8::ConstructorBehavior::kThrow,
502 v8::SideEffectType::kHasSideEffect);
503 // kInternalized strings are created in the old space.
504 const v8::NewStringType type = v8::NewStringType::kInternalized;
505 Local<v8::String> name_string =
506 v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked();
507 that->InstanceTemplate()->Set(name_string, t);
508 t->SetClassName(name_string);
509 }
510
SetConstructorFunction(Local<v8::Context> context, Local<v8::Object> that, const char* name, Local<v8::FunctionTemplate> tmpl, SetConstructorFunctionFlag flag)511 void SetConstructorFunction(Local<v8::Context> context,
512 Local<v8::Object> that,
513 const char* name,
514 Local<v8::FunctionTemplate> tmpl,
515 SetConstructorFunctionFlag flag) {
516 Isolate* isolate = context->GetIsolate();
517 SetConstructorFunction(
518 context, that, OneByteString(isolate, name), tmpl, flag);
519 }
520
SetConstructorFunction(Local<v8::Context> context, Local<v8::Object> that, Local<v8::String> name, Local<v8::FunctionTemplate> tmpl, SetConstructorFunctionFlag flag)521 void SetConstructorFunction(Local<v8::Context> context,
522 Local<v8::Object> that,
523 Local<v8::String> name,
524 Local<v8::FunctionTemplate> tmpl,
525 SetConstructorFunctionFlag flag) {
526 if (LIKELY(flag == SetConstructorFunctionFlag::SET_CLASS_NAME))
527 tmpl->SetClassName(name);
528 that->Set(context, name, tmpl->GetFunction(context).ToLocalChecked()).Check();
529 }
530
531 namespace {
532
533 class NonOwningExternalOneByteResource
534 : public v8::String::ExternalOneByteStringResource {
535 public:
NonOwningExternalOneByteResource(const UnionBytes& source)536 explicit NonOwningExternalOneByteResource(const UnionBytes& source)
537 : source_(source) {}
538 ~NonOwningExternalOneByteResource() override = default;
539
540 const char* data() const override {
541 return reinterpret_cast<const char*>(source_.one_bytes_data());
542 }
543 size_t length() const override { return source_.length(); }
544
545 NonOwningExternalOneByteResource(const NonOwningExternalOneByteResource&) =
546 delete;
547 NonOwningExternalOneByteResource& operator=(
548 const NonOwningExternalOneByteResource&) = delete;
549
550 private:
551 const UnionBytes source_;
552 };
553
554 class NonOwningExternalTwoByteResource
555 : public v8::String::ExternalStringResource {
556 public:
NonOwningExternalTwoByteResource(const UnionBytes& source)557 explicit NonOwningExternalTwoByteResource(const UnionBytes& source)
558 : source_(source) {}
559 ~NonOwningExternalTwoByteResource() override = default;
560
561 const uint16_t* data() const override { return source_.two_bytes_data(); }
562 size_t length() const override { return source_.length(); }
563
564 NonOwningExternalTwoByteResource(const NonOwningExternalTwoByteResource&) =
565 delete;
566 NonOwningExternalTwoByteResource& operator=(
567 const NonOwningExternalTwoByteResource&) = delete;
568
569 private:
570 const UnionBytes source_;
571 };
572
573 } // anonymous namespace
574
ToStringChecked(Isolate* isolate) const575 Local<String> UnionBytes::ToStringChecked(Isolate* isolate) const {
576 if (UNLIKELY(length() == 0)) {
577 // V8 requires non-null data pointers for empty external strings,
578 // but we don't guarantee that. Solve this by not creating an
579 // external string at all in that case.
580 return String::Empty(isolate);
581 }
582 if (is_one_byte()) {
583 NonOwningExternalOneByteResource* source =
584 new NonOwningExternalOneByteResource(*this);
585 return String::NewExternalOneByte(isolate, source).ToLocalChecked();
586 } else {
587 NonOwningExternalTwoByteResource* source =
588 new NonOwningExternalTwoByteResource(*this);
589 return String::NewExternalTwoByte(isolate, source).ToLocalChecked();
590 }
591 }
592
593 } // namespace node
594