1 #include "node_util.h"
2 #include "base_object-inl.h"
3 #include "node_errors.h"
4 #include "node_external_reference.h"
5 #include "util-inl.h"
6
7 namespace node {
8 namespace util {
9
10 using v8::ALL_PROPERTIES;
11 using v8::Array;
12 using v8::ArrayBufferView;
13 using v8::BigInt;
14 using v8::Boolean;
15 using v8::Context;
16 using v8::External;
17 using v8::FunctionCallbackInfo;
18 using v8::FunctionTemplate;
19 using v8::HandleScope;
20 using v8::IndexFilter;
21 using v8::Integer;
22 using v8::Isolate;
23 using v8::KeyCollectionMode;
24 using v8::Local;
25 using v8::Object;
26 using v8::ONLY_CONFIGURABLE;
27 using v8::ONLY_ENUMERABLE;
28 using v8::ONLY_WRITABLE;
29 using v8::Promise;
30 using v8::PropertyFilter;
31 using v8::Proxy;
32 using v8::SKIP_STRINGS;
33 using v8::SKIP_SYMBOLS;
34 using v8::String;
35 using v8::Uint32;
36 using v8::Value;
37
38 // Used in ToUSVString().
39 constexpr char16_t kUnicodeReplacementCharacter = 0xFFFD;
40
41 // If a UTF-16 character is a low/trailing surrogate.
42 CHAR_TEST(16, IsUnicodeTrail, (ch & 0xFC00) == 0xDC00)
43
44 // If a UTF-16 character is a surrogate.
45 CHAR_TEST(16, IsUnicodeSurrogate, (ch & 0xF800) == 0xD800)
46
47 // If a UTF-16 surrogate is a low/trailing one.
48 CHAR_TEST(16, IsUnicodeSurrogateTrail, (ch & 0x400) != 0)
49
GetOwnNonIndexProperties(const FunctionCallbackInfo<Value> & args)50 static void GetOwnNonIndexProperties(
51 const FunctionCallbackInfo<Value>& args) {
52 Environment* env = Environment::GetCurrent(args);
53 Local<Context> context = env->context();
54
55 CHECK(args[0]->IsObject());
56 CHECK(args[1]->IsUint32());
57
58 Local<Object> object = args[0].As<Object>();
59
60 Local<Array> properties;
61
62 PropertyFilter filter =
63 static_cast<PropertyFilter>(args[1].As<Uint32>()->Value());
64
65 if (!object->GetPropertyNames(
66 context, KeyCollectionMode::kOwnOnly,
67 filter,
68 IndexFilter::kSkipIndices)
69 .ToLocal(&properties)) {
70 return;
71 }
72 args.GetReturnValue().Set(properties);
73 }
74
GetConstructorName(const FunctionCallbackInfo<Value> & args)75 static void GetConstructorName(
76 const FunctionCallbackInfo<Value>& args) {
77 CHECK(args[0]->IsObject());
78
79 Local<Object> object = args[0].As<Object>();
80 Local<String> name = object->GetConstructorName();
81
82 args.GetReturnValue().Set(name);
83 }
84
GetExternalValue(const FunctionCallbackInfo<Value> & args)85 static void GetExternalValue(
86 const FunctionCallbackInfo<Value>& args) {
87 CHECK(args[0]->IsExternal());
88 Isolate* isolate = args.GetIsolate();
89 Local<External> external = args[0].As<External>();
90
91 void* ptr = external->Value();
92 uint64_t value = reinterpret_cast<uint64_t>(ptr);
93 Local<BigInt> ret = BigInt::NewFromUnsigned(isolate, value);
94 args.GetReturnValue().Set(ret);
95 }
96
GetPromiseDetails(const FunctionCallbackInfo<Value> & args)97 static void GetPromiseDetails(const FunctionCallbackInfo<Value>& args) {
98 // Return undefined if it's not a Promise.
99 if (!args[0]->IsPromise())
100 return;
101
102 auto isolate = args.GetIsolate();
103
104 Local<Promise> promise = args[0].As<Promise>();
105
106 int state = promise->State();
107 Local<Value> values[2] = { Integer::New(isolate, state) };
108 size_t number_of_values = 1;
109 if (state != Promise::PromiseState::kPending)
110 values[number_of_values++] = promise->Result();
111 Local<Array> ret = Array::New(isolate, values, number_of_values);
112 args.GetReturnValue().Set(ret);
113 }
114
GetProxyDetails(const FunctionCallbackInfo<Value> & args)115 static void GetProxyDetails(const FunctionCallbackInfo<Value>& args) {
116 // Return undefined if it's not a proxy.
117 if (!args[0]->IsProxy())
118 return;
119
120 Local<Proxy> proxy = args[0].As<Proxy>();
121
122 // TODO(BridgeAR): Remove the length check as soon as we prohibit access to
123 // the util binding layer. It's accessed in the wild and `esm` would break in
124 // case the check is removed.
125 if (args.Length() == 1 || args[1]->IsTrue()) {
126 Local<Value> ret[] = {
127 proxy->GetTarget(),
128 proxy->GetHandler()
129 };
130
131 args.GetReturnValue().Set(
132 Array::New(args.GetIsolate(), ret, arraysize(ret)));
133 } else {
134 Local<Value> ret = proxy->GetTarget();
135
136 args.GetReturnValue().Set(ret);
137 }
138 }
139
IsArrayBufferDetached(const FunctionCallbackInfo<Value> & args)140 static void IsArrayBufferDetached(const FunctionCallbackInfo<Value>& args) {
141 if (args[0]->IsArrayBuffer()) {
142 auto buffer = args[0].As<v8::ArrayBuffer>();
143 args.GetReturnValue().Set(buffer->WasDetached());
144 return;
145 }
146 args.GetReturnValue().Set(false);
147 }
148
PreviewEntries(const FunctionCallbackInfo<Value> & args)149 static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
150 if (!args[0]->IsObject())
151 return;
152
153 Environment* env = Environment::GetCurrent(args);
154 bool is_key_value;
155 Local<Array> entries;
156 if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
157 return;
158 // Fast path for WeakMap and WeakSet.
159 if (args.Length() == 1)
160 return args.GetReturnValue().Set(entries);
161
162 Local<Value> ret[] = {
163 entries,
164 Boolean::New(env->isolate(), is_key_value)
165 };
166 return args.GetReturnValue().Set(
167 Array::New(env->isolate(), ret, arraysize(ret)));
168 }
169
Sleep(const FunctionCallbackInfo<Value> & args)170 static void Sleep(const FunctionCallbackInfo<Value>& args) {
171 CHECK(args[0]->IsUint32());
172 uint32_t msec = args[0].As<Uint32>()->Value();
173 uv_sleep(msec);
174 }
175
ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value> & args)176 void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
177 CHECK(args[0]->IsArrayBufferView());
178 args.GetReturnValue().Set(args[0].As<ArrayBufferView>()->HasBuffer());
179 }
180
WeakReference(Realm * realm,Local<Object> object,Local<Object> target)181 WeakReference::WeakReference(Realm* realm,
182 Local<Object> object,
183 Local<Object> target)
184 : WeakReference(realm, object, target, 0) {}
185
WeakReference(Realm * realm,Local<Object> object,Local<Object> target,uint64_t reference_count)186 WeakReference::WeakReference(Realm* realm,
187 Local<Object> object,
188 Local<Object> target,
189 uint64_t reference_count)
190 : SnapshotableObject(realm, object, type_int),
191 reference_count_(reference_count) {
192 MakeWeak();
193 if (!target.IsEmpty()) {
194 target_.Reset(realm->isolate(), target);
195 if (reference_count_ == 0) {
196 target_.SetWeak();
197 }
198 }
199 }
200
PrepareForSerialization(Local<Context> context,v8::SnapshotCreator * creator)201 bool WeakReference::PrepareForSerialization(Local<Context> context,
202 v8::SnapshotCreator* creator) {
203 if (target_.IsEmpty()) {
204 target_index_ = 0;
205 return true;
206 }
207
208 // Users can still hold strong references to target in addition to the
209 // reference that we manage here, and they could expect that the referenced
210 // object remains the same as long as that external strong reference
211 // is alive. Since we have no way to know if there is any other reference
212 // keeping the target alive, the best we can do to maintain consistency is to
213 // simply save a reference to the target in the snapshot (effectively making
214 // it strong) during serialization, and restore it during deserialization.
215 // If there's no known counted reference from our side, we'll make the
216 // reference here weak upon deserialization so that it can be GC'ed if users
217 // do not hold additional references to it.
218 Local<Object> target = target_.Get(context->GetIsolate());
219 target_index_ = creator->AddData(context, target);
220 DCHECK_NE(target_index_, 0);
221 target_.Reset();
222 return true;
223 }
224
Serialize(int index)225 InternalFieldInfoBase* WeakReference::Serialize(int index) {
226 DCHECK_EQ(index, BaseObject::kEmbedderType);
227 InternalFieldInfo* info =
228 InternalFieldInfoBase::New<InternalFieldInfo>(type());
229 info->target = target_index_;
230 info->reference_count = reference_count_;
231 return info;
232 }
233
Deserialize(Local<Context> context,Local<Object> holder,int index,InternalFieldInfoBase * info)234 void WeakReference::Deserialize(Local<Context> context,
235 Local<Object> holder,
236 int index,
237 InternalFieldInfoBase* info) {
238 DCHECK_EQ(index, BaseObject::kEmbedderType);
239 HandleScope scope(context->GetIsolate());
240
241 InternalFieldInfo* weak_info = reinterpret_cast<InternalFieldInfo*>(info);
242 Local<Object> target;
243 if (weak_info->target != 0) {
244 target = context->GetDataFromSnapshotOnce<Object>(weak_info->target)
245 .ToLocalChecked();
246 }
247 new WeakReference(
248 Realm::GetCurrent(context), holder, target, weak_info->reference_count);
249 }
250
New(const FunctionCallbackInfo<Value> & args)251 void WeakReference::New(const FunctionCallbackInfo<Value>& args) {
252 Realm* realm = Realm::GetCurrent(args);
253 CHECK(args.IsConstructCall());
254 CHECK(args[0]->IsObject());
255 new WeakReference(realm, args.This(), args[0].As<Object>());
256 }
257
Get(const FunctionCallbackInfo<Value> & args)258 void WeakReference::Get(const FunctionCallbackInfo<Value>& args) {
259 WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
260 Isolate* isolate = args.GetIsolate();
261 if (!weak_ref->target_.IsEmpty())
262 args.GetReturnValue().Set(weak_ref->target_.Get(isolate));
263 }
264
GetRef(const FunctionCallbackInfo<Value> & args)265 void WeakReference::GetRef(const FunctionCallbackInfo<Value>& args) {
266 WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
267 Isolate* isolate = args.GetIsolate();
268 args.GetReturnValue().Set(
269 v8::Number::New(isolate, weak_ref->reference_count_));
270 }
271
IncRef(const FunctionCallbackInfo<Value> & args)272 void WeakReference::IncRef(const FunctionCallbackInfo<Value>& args) {
273 WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
274 weak_ref->reference_count_++;
275 if (weak_ref->target_.IsEmpty()) return;
276 if (weak_ref->reference_count_ == 1) weak_ref->target_.ClearWeak();
277 }
278
DecRef(const FunctionCallbackInfo<Value> & args)279 void WeakReference::DecRef(const FunctionCallbackInfo<Value>& args) {
280 WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
281 CHECK_GE(weak_ref->reference_count_, 1);
282 weak_ref->reference_count_--;
283 if (weak_ref->target_.IsEmpty()) return;
284 if (weak_ref->reference_count_ == 0) weak_ref->target_.SetWeak();
285 }
286
GuessHandleType(const FunctionCallbackInfo<Value> & args)287 static void GuessHandleType(const FunctionCallbackInfo<Value>& args) {
288 Environment* env = Environment::GetCurrent(args);
289 int fd;
290 if (!args[0]->Int32Value(env->context()).To(&fd)) return;
291 CHECK_GE(fd, 0);
292
293 uv_handle_type t = uv_guess_handle(fd);
294 const char* type = nullptr;
295
296 switch (t) {
297 case UV_TCP:
298 type = "TCP";
299 break;
300 case UV_TTY:
301 type = "TTY";
302 break;
303 case UV_UDP:
304 type = "UDP";
305 break;
306 case UV_FILE:
307 type = "FILE";
308 break;
309 case UV_NAMED_PIPE:
310 type = "PIPE";
311 break;
312 case UV_UNKNOWN_HANDLE:
313 type = "UNKNOWN";
314 break;
315 default:
316 ABORT();
317 }
318
319 args.GetReturnValue().Set(OneByteString(env->isolate(), type));
320 }
321
ToUSVString(const FunctionCallbackInfo<Value> & args)322 static void ToUSVString(const FunctionCallbackInfo<Value>& args) {
323 Environment* env = Environment::GetCurrent(args);
324 CHECK_GE(args.Length(), 2);
325 CHECK(args[0]->IsString());
326 CHECK(args[1]->IsNumber());
327
328 TwoByteValue value(env->isolate(), args[0]);
329
330 int64_t start = args[1]->IntegerValue(env->context()).FromJust();
331 CHECK_GE(start, 0);
332
333 for (size_t i = start; i < value.length(); i++) {
334 char16_t c = value[i];
335 if (!IsUnicodeSurrogate(c)) {
336 continue;
337 } else if (IsUnicodeSurrogateTrail(c) || i == value.length() - 1) {
338 value[i] = kUnicodeReplacementCharacter;
339 } else {
340 char16_t d = value[i + 1];
341 if (IsUnicodeTrail(d)) {
342 i++;
343 } else {
344 value[i] = kUnicodeReplacementCharacter;
345 }
346 }
347 }
348
349 args.GetReturnValue().Set(
350 String::NewFromTwoByte(env->isolate(),
351 *value,
352 v8::NewStringType::kNormal,
353 value.length()).ToLocalChecked());
354 }
355
RegisterExternalReferences(ExternalReferenceRegistry * registry)356 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
357 registry->Register(GetPromiseDetails);
358 registry->Register(GetProxyDetails);
359 registry->Register(IsArrayBufferDetached);
360 registry->Register(PreviewEntries);
361 registry->Register(GetOwnNonIndexProperties);
362 registry->Register(GetConstructorName);
363 registry->Register(GetExternalValue);
364 registry->Register(Sleep);
365 registry->Register(ArrayBufferViewHasBuffer);
366 registry->Register(WeakReference::New);
367 registry->Register(WeakReference::Get);
368 registry->Register(WeakReference::GetRef);
369 registry->Register(WeakReference::IncRef);
370 registry->Register(WeakReference::DecRef);
371 registry->Register(GuessHandleType);
372 registry->Register(ToUSVString);
373 }
374
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)375 void Initialize(Local<Object> target,
376 Local<Value> unused,
377 Local<Context> context,
378 void* priv) {
379 Environment* env = Environment::GetCurrent(context);
380 Isolate* isolate = env->isolate();
381
382 {
383 Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate);
384 #define V(PropertyName, _) \
385 tmpl->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #PropertyName), \
386 env->PropertyName());
387
388 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
389 #undef V
390
391 target
392 ->Set(context,
393 FIXED_ONE_BYTE_STRING(isolate, "privateSymbols"),
394 tmpl->NewInstance(context).ToLocalChecked())
395 .Check();
396 }
397
398 {
399 Local<Object> constants = Object::New(isolate);
400 #define V(name) \
401 constants \
402 ->Set(context, \
403 FIXED_ONE_BYTE_STRING(isolate, #name), \
404 Integer::New(isolate, Promise::PromiseState::name)) \
405 .Check();
406
407 V(kPending);
408 V(kFulfilled);
409 V(kRejected);
410 #undef V
411
412 #define V(name) \
413 constants \
414 ->Set(context, \
415 FIXED_ONE_BYTE_STRING(isolate, #name), \
416 Integer::New(isolate, PropertyFilter::name)) \
417 .Check();
418
419 V(ALL_PROPERTIES);
420 V(ONLY_WRITABLE);
421 V(ONLY_ENUMERABLE);
422 V(ONLY_CONFIGURABLE);
423 V(SKIP_STRINGS);
424 V(SKIP_SYMBOLS);
425 #undef V
426
427 target->Set(context, env->constants_string(), constants).Check();
428 }
429
430 SetMethodNoSideEffect(
431 context, target, "getPromiseDetails", GetPromiseDetails);
432 SetMethodNoSideEffect(context, target, "getProxyDetails", GetProxyDetails);
433 SetMethodNoSideEffect(
434 context, target, "isArrayBufferDetached", IsArrayBufferDetached);
435 SetMethodNoSideEffect(context, target, "previewEntries", PreviewEntries);
436 SetMethodNoSideEffect(
437 context, target, "getOwnNonIndexProperties", GetOwnNonIndexProperties);
438 SetMethodNoSideEffect(
439 context, target, "getConstructorName", GetConstructorName);
440 SetMethodNoSideEffect(context, target, "getExternalValue", GetExternalValue);
441 SetMethod(context, target, "sleep", Sleep);
442
443 SetMethod(
444 context, target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer);
445
446 Local<String> should_abort_on_uncaught_toggle =
447 FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle");
448 CHECK(target
449 ->Set(context,
450 should_abort_on_uncaught_toggle,
451 env->should_abort_on_uncaught_toggle().GetJSArray())
452 .FromJust());
453
454 Local<FunctionTemplate> weak_ref =
455 NewFunctionTemplate(isolate, WeakReference::New);
456 weak_ref->InstanceTemplate()->SetInternalFieldCount(
457 WeakReference::kInternalFieldCount);
458 weak_ref->Inherit(BaseObject::GetConstructorTemplate(env));
459 SetProtoMethod(isolate, weak_ref, "get", WeakReference::Get);
460 SetProtoMethod(isolate, weak_ref, "getRef", WeakReference::GetRef);
461 SetProtoMethod(isolate, weak_ref, "incRef", WeakReference::IncRef);
462 SetProtoMethod(isolate, weak_ref, "decRef", WeakReference::DecRef);
463 SetConstructorFunction(context, target, "WeakReference", weak_ref);
464
465 SetMethod(context, target, "guessHandleType", GuessHandleType);
466
467 SetMethodNoSideEffect(context, target, "toUSVString", ToUSVString);
468 }
469
470 } // namespace util
471 } // namespace node
472
473 NODE_BINDING_CONTEXT_AWARE_INTERNAL(util, node::util::Initialize)
474 NODE_BINDING_EXTERNAL_REFERENCE(util, node::util::RegisterExternalReferences)
475