• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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