Repository: nodejs/node
Pull request: src: update cares_wrap to use DictionaryTemplates

Updates cares_wrap to use DictionaryTemplates

Changed files:
filename: src/cares_wrap.cc
patch: @@ -63,6 +63,7 @@ namespace cares_wrap {
 using v8::Array;
 using v8::ArrayBuffer;
 using v8::Context;
+using v8::DictionaryTemplate;
 using v8::EscapableHandleScope;
 using v8::Exception;
 using v8::FunctionCallbackInfo;
@@ -82,6 +83,7 @@ using v8::Null;
 using v8::Object;
 using v8::String;
 using v8::Uint32;
+using v8::Undefined;
 using v8::Value;
 
 namespace {
@@ -303,41 +305,43 @@ Maybe<int> ParseMxReply(Environment* env,
                         bool need_type = false) {
   HandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->mx_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "exchange",
+        "priority",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_mx_record_template(tmpl);
+  }
+
   struct ares_mx_reply* mx_start;
   int status = ares_parse_mx_reply(buf, len, &mx_start);
   if (status != ARES_SUCCESS) return Just<int>(status);
 
+  DeleteFnPtr<void, ares_free_data> free_me(mx_start);
+
   uint32_t offset = ret->Length();
   ares_mx_reply* current = mx_start;
-  for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
-    Local<Object> mx_record = Object::New(env->isolate());
-    if (mx_record
-            ->Set(env->context(),
-                  env->exchange_string(),
-                  OneByteString(env->isolate(), current->host))
-            .IsNothing() ||
-        mx_record
-            ->Set(env->context(),
-                  env->priority_string(),
-                  Integer::New(env->isolate(), current->priority))
-            .IsNothing()) {
-      ares_free_data(mx_start);
-      return Nothing<int>();
-    }
-    if (need_type &&
-        mx_record->Set(env->context(), env->type_string(), env->dns_mx_string())
-            .IsNothing()) {
-      ares_free_data(mx_start);
-      return Nothing<int>();
-    }
 
-    if (ret->Set(env->context(), i + offset, mx_record).IsNothing()) {
-      ares_free_data(mx_start);
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // exchange
+      Undefined(env->isolate()),  // priority
+      Undefined(env->isolate()),  // type
+  };
+
+  for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
+    values[0] = OneByteString(env->isolate(), current->host);
+    values[1] = Integer::New(env->isolate(), current->priority);
+    values[2] = env->dns_mx_string();
+    Local<Value> record;
+    if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&record) ||
+        ret->Set(env->context(), i + offset, record).IsNothing()) {
       return Nothing<int>();
     }
   }
 
-  ares_free_data(mx_start);
   return Just<int>(ARES_SUCCESS);
 }
 
@@ -348,43 +352,52 @@ Maybe<int> ParseCaaReply(Environment* env,
                          bool need_type = false) {
   HandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->caa_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "critical",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_caa_record_template(tmpl);
+  }
+
   struct ares_caa_reply* caa_start;
   int status = ares_parse_caa_reply(buf, len, &caa_start);
   if (status != ARES_SUCCESS) return Just<int>(status);
+  DeleteFnPtr<void, ares_free_data> free_me(caa_start);
+
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // critical
+      Undefined(env->isolate()),  // type
+  };
 
   uint32_t offset = ret->Length();
   ares_caa_reply* current = caa_start;
   for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
-    Local<Object> caa_record = Object::New(env->isolate());
+    values[0] = Integer::New(env->isolate(), current->critical);
+    values[1] = env->dns_caa_string();
+    Local<Object> caa_record;
+    if (!NewDictionaryInstance(env->context(), tmpl, values)
+             .ToLocal(&caa_record)) {
+      return Nothing<int>();
+    }
 
+    // This additional property is not part of the template as it is
+    // variable based on the record.
     if (caa_record
-            ->Set(env->context(),
-                  env->dns_critical_string(),
-                  Integer::New(env->isolate(), current->critical))
-            .IsNothing() ||
-        caa_record
             ->Set(env->context(),
                   OneByteString(env->isolate(), current->property),
                   OneByteString(env->isolate(), current->value))
             .IsNothing()) {
-      ares_free_data(caa_start);
-      return Nothing<int>();
-    }
-    if (need_type &&
-        caa_record
-            ->Set(env->context(), env->type_string(), env->dns_caa_string())
-            .IsNothing()) {
-      ares_free_data(caa_start);
       return Nothing<int>();
     }
 
     if (ret->Set(env->context(), i + offset, caa_record).IsNothing()) {
-      ares_free_data(caa_start);
       return Nothing<int>();
     }
   }
 
-  ares_free_data(caa_start);
   return Just<int>(ARES_SUCCESS);
 }
 
@@ -394,6 +407,18 @@ Maybe<int> ParseTlsaReply(Environment* env,
                           Local<Array> ret) {
   EscapableHandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->tlsa_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "certUsage",
+        "selector",
+        "match",
+        "data",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_tlsa_record_template(tmpl);
+  }
+
   ares_dns_record_t* dnsrec = nullptr;
 
   int status = ares_dns_parse(buf, len, 0, &dnsrec);
@@ -402,9 +427,18 @@ Maybe<int> ParseTlsaReply(Environment* env,
     return Just<int>(status);
   }
 
+  DeleteFnPtr<ares_dns_record_t, ares_dns_record_destroy> free_me(dnsrec);
+
   uint32_t offset = ret->Length();
   size_t rr_count = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
 
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // certUsage
+      Undefined(env->isolate()),  // selector
+      Undefined(env->isolate()),  // match
+      Undefined(env->isolate()),  // data
+  };
+
   for (size_t i = 0; i < rr_count; i++) {
     const ares_dns_rr_t* rr =
         ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i);
@@ -422,32 +456,19 @@ Maybe<int> ParseTlsaReply(Environment* env,
     Local<ArrayBuffer> data_ab = ArrayBuffer::New(env->isolate(), data_len);
     memcpy(data_ab->Data(), data, data_len);
 
-    Local<Object> tlsa_rec = Object::New(env->isolate());
+    values[0] = Integer::NewFromUnsigned(env->isolate(), certusage);
+    values[1] = Integer::NewFromUnsigned(env->isolate(), selector);
+    values[2] = Integer::NewFromUnsigned(env->isolate(), match);
+    values[3] = data_ab;
 
-    if (tlsa_rec
-            ->Set(env->context(),
-                  env->cert_usage_string(),
-                  Integer::NewFromUnsigned(env->isolate(), certusage))
-            .IsNothing() ||
-        tlsa_rec
-            ->Set(env->context(),
-                  env->selector_string(),
-                  Integer::NewFromUnsigned(env->isolate(), selector))
-            .IsNothing() ||
-        tlsa_rec
-            ->Set(env->context(),
-                  env->match_string(),
-                  Integer::NewFromUnsigned(env->isolate(), match))
-            .IsNothing() ||
-        tlsa_rec->Set(env->context(), env->data_string(), data_ab)
-            .IsNothing() ||
+    Local<Object> tlsa_rec;
+    if (!NewDictionaryInstance(env->context(), tmpl, values)
+             .ToLocal(&tlsa_rec) ||
         ret->Set(env->context(), offset + i, tlsa_rec).IsNothing()) {
-      ares_dns_record_destroy(dnsrec);
       return Nothing<int>();
     }
   }
 
-  ares_dns_record_destroy(dnsrec);
   return Just<int>(ARES_SUCCESS);
 }
 
@@ -458,70 +479,81 @@ Maybe<int> ParseTxtReply(Environment* env,
                          bool need_type = false) {
   HandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->txt_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "entries",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_txt_record_template(tmpl);
+  }
+
   struct ares_txt_ext* txt_out;
 
   int status = ares_parse_txt_reply_ext(buf, len, &txt_out);
   if (status != ARES_SUCCESS) return Just<int>(status);
+  DeleteFnPtr<void, ares_free_data> free_me(txt_out);
 
   Local<Array> txt_chunk;
+  LocalVector<Value> chunks(env->isolate());
 
   struct ares_txt_ext* current = txt_out;
-  uint32_t i = 0, j;
+  uint32_t i = 0;
   uint32_t offset = ret->Length();
-  for (j = 0; current != nullptr; current = current->next) {
+
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // entries
+      Undefined(env->isolate()),  // type
+  };
+
+  for (; current != nullptr; current = current->next) {
     Local<String> txt =
         OneByteString(env->isolate(), current->txt, current->length);
 
     // New record found - write out the current chunk
     if (current->record_start) {
-      if (!txt_chunk.IsEmpty()) {
+      if (!chunks.empty()) {
+        auto txt_chunk =
+            Array::New(env->isolate(), chunks.data(), chunks.size());
+        chunks.clear();
         if (need_type) {
-          Local<Object> elem = Object::New(env->isolate());
-          if (elem->Set(env->context(), env->entries_string(), txt_chunk)
-                  .IsNothing() ||
-              elem->Set(
-                      env->context(), env->type_string(), env->dns_txt_string())
-                  .IsNothing() ||
+          values[0] = txt_chunk;
+          values[1] = env->dns_txt_string();
+          Local<Object> elem;
+          if (!NewDictionaryInstance(env->context(), tmpl, values)
+                   .ToLocal(&elem) ||
               ret->Set(env->context(), offset + i++, elem).IsNothing()) {
-            ares_free_data(txt_out);
             return Nothing<int>();
           }
         } else if (ret->Set(env->context(), offset + i++, txt_chunk)
                        .IsNothing()) {
-          ares_free_data(txt_out);
           return Nothing<int>();
         }
       }
 
       txt_chunk = Array::New(env->isolate());
-      j = 0;
     }
 
-    if (txt_chunk->Set(env->context(), j++, txt).IsNothing()) {
-      ares_free_data(txt_out);
-      return Nothing<int>();
-    }
+    chunks.push_back(txt);
   }
 
   // Push last chunk if it isn't empty
-  if (!txt_chunk.IsEmpty()) {
+  if (!chunks.empty()) {
+    txt_chunk = Array::New(env->isolate(), chunks.data(), chunks.size());
     if (need_type) {
-      Local<Object> elem = Object::New(env->isolate());
-      if (elem->Set(env->context(), env->entries_string(), txt_chunk)
-              .IsNothing() ||
-          elem->Set(env->context(), env->type_string(), env->dns_txt_string())
-              .IsNothing() ||
+      values[0] = txt_chunk;
+      values[1] = env->dns_txt_string();
+      Local<Object> elem;
+      if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&elem) ||
           ret->Set(env->context(), offset + i, elem).IsNothing()) {
-        ares_free_data(txt_out);
         return Nothing<int>();
       }
     } else if (ret->Set(env->context(), offset + i, txt_chunk).IsNothing()) {
-      ares_free_data(txt_out);
       return Nothing<int>();
     }
   }
 
-  ares_free_data(txt_out);
   return Just<int>(ARES_SUCCESS);
 }
 
@@ -532,53 +564,49 @@ Maybe<int> ParseSrvReply(Environment* env,
                          bool need_type = false) {
   HandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->srv_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "name",
+        "port",
+        "priority",
+        "weight",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_srv_record_template(tmpl);
+  }
+
   struct ares_srv_reply* srv_start;
   int status = ares_parse_srv_reply(buf, len, &srv_start);
   if (status != ARES_SUCCESS) return Just<int>(status);
+  DeleteFnPtr<void, ares_free_data> free_me(srv_start);
+
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // name
+      Undefined(env->isolate()),  // port
+      Undefined(env->isolate()),  // priority
+      Undefined(env->isolate()),  // weight
+      Undefined(env->isolate()),  // type
+  };
 
   ares_srv_reply* current = srv_start;
   int offset = ret->Length();
   for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
-    Local<Object> srv_record = Object::New(env->isolate());
-
-    if (srv_record
-            ->Set(env->context(),
-                  env->name_string(),
-                  OneByteString(env->isolate(), current->host))
-            .IsNothing() ||
-        srv_record
-            ->Set(env->context(),
-                  env->port_string(),
-                  Integer::New(env->isolate(), current->port))
-            .IsNothing() ||
-        srv_record
-            ->Set(env->context(),
-                  env->priority_string(),
-                  Integer::New(env->isolate(), current->priority))
-            .IsNothing() ||
-        srv_record
-            ->Set(env->context(),
-                  env->weight_string(),
-                  Integer::New(env->isolate(), current->weight))
-            .IsNothing()) {
-      ares_free_data(srv_start);
-      return Nothing<int>();
-    }
-    if (need_type &&
-        srv_record
-            ->Set(env->context(), env->type_string(), env->dns_srv_string())
-            .IsNothing()) {
-      ares_free_data(srv_start);
-      return Nothing<int>();
-    }
-
-    if (ret->Set(env->context(), i + offset, srv_record).IsNothing()) {
-      ares_free_data(srv_start);
+    values[0] = OneByteString(env->isolate(), current->host);
+    values[1] = Integer::New(env->isolate(), current->port);
+    values[2] = Integer::New(env->isolate(), current->priority);
+    values[3] = Integer::New(env->isolate(), current->weight);
+    values[4] = env->dns_srv_string();
+
+    Local<Object> srv_record;
+    if (!NewDictionaryInstance(env->context(), tmpl, values)
+             .ToLocal(&srv_record) ||
+        ret->Set(env->context(), i + offset, srv_record).IsNothing()) {
       return Nothing<int>();
     }
   }
 
-  ares_free_data(srv_start);
   return Just<int>(ARES_SUCCESS);
 }
 
@@ -589,73 +617,87 @@ Maybe<int> ParseNaptrReply(Environment* env,
                            bool need_type = false) {
   HandleScope handle_scope(env->isolate());
 
+  auto tmpl = env->naptr_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "flags",
+        "service",
+        "regexp",
+        "replacement",
+        "order",
+        "preference",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_naptr_record_template(tmpl);
+  }
+
   ares_naptr_reply* naptr_start;
   int status = ares_parse_naptr_reply(buf, len, &naptr_start);
-
   if (status != ARES_SUCCESS) return Just<int>(status);
+  DeleteFnPtr<void, ares_free_data> free_me(naptr_start);
+
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // flags
+      Undefined(env->isolate()),  // service
+      Undefined(env->isolate()),  // regexp
+      Undefined(env->isolate()),  // replacement
+      Undefined(env->isolate()),  // order
+      Undefined(env->isolate()),  // preference
+      Undefined(env->isolate()),  // type
+  };
 
   ares_naptr_reply* current = naptr_start;
   int offset = ret->Length();
   for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
-    Local<Object> naptr_record = Object::New(env->isolate());
-
-    if (naptr_record
-            ->Set(env->context(),
-                  env->flags_string(),
-                  OneByteString(env->isolate(), current->flags))
-            .IsNothing() ||
-        naptr_record
-            ->Set(env->context(),
-                  env->service_string(),
-                  OneByteString(env->isolate(), current->service))
-            .IsNothing() ||
-        naptr_record
-            ->Set(env->context(),
-                  env->regexp_string(),
-                  OneByteString(env->isolate(), current->regexp))
-            .IsNothing() ||
-        naptr_record
-            ->Set(env->context(),
-                  env->replacement_string(),
-                  OneByteString(env->isolate(), current->replacement))
-            .IsNothing() ||
-        naptr_record
-            ->Set(env->context(),
-                  env->order_string(),
-                  Integer::New(env->isolate(), current->order))
-            .IsNothing() ||
-        naptr_record
-            ->Set(env->context(),
-                  env->preference_string(),
-                  Integer::New(env->isolate(), current->preference))
-            .IsNothing()) {
-      ares_free_data(naptr_start);
-      return Nothing<int>();
-    }
-    if (need_type &&
-        naptr_record
-            ->Set(env->context(), env->type_string(), env->dns_naptr_string())
-            .IsNothing()) {
-      ares_free_data(naptr_start);
-      return Nothing<int>();
+    values[0] = OneByteString(env->isolate(), current->flags);
+    values[1] = OneByteString(env->isolate(), current->service);
+    values[2] = OneByteString(env->isolate(), current->regexp);
+    values[3] = OneByteString(env->isolate(), current->replacement);
+    values[4] = Integer::New(env->isolate(), current->order);
+    values[5] = Integer::New(env->isolate(), current->preference);
+    if (need_type) {
+      values[6] = env->dns_naptr_string();
     }
 
-    if (ret->Set(env->context(), i + offset, naptr_record).IsNothing()) {
-      ares_free_data(naptr_start);
+    Local<Object> naptr_record;
+    if (!NewDictionaryInstance(env->context(), tmpl, values)
+             .ToLocal(&naptr_record) ||
+        ret->Set(env->context(), i + offset, naptr_record).IsNothing()) {
       return Nothing<int>();
     }
   }
 
-  ares_free_data(naptr_start);
   return Just<int>(ARES_SUCCESS);
 }
 
+Local<DictionaryTemplate> getSoaRecordTemplate(Environment* env) {
+  auto tmpl = env->soa_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "nsname",
+        "hostmaster",
+        "serial",
+        "refresh",
+        "retry",
+        "expire",
+        "minttl",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_soa_record_template(tmpl);
+  }
+  return tmpl;
+}
+
 Maybe<int> ParseSoaReply(Environment* env,
                          unsigned char* buf,
                          int len,
                          Local<Object>* ret) {
   EscapableHandleScope handle_scope(env->isolate());
 
+  auto tmpl = getSoaRecordTemplate(env);
+
   // Manage memory using standardard smart pointer std::unique_tr
   struct AresDeleter {
     void operator()(char* ptr) const noexcept { ares_free_string(ptr); }
@@ -680,6 +722,17 @@ Maybe<int> ParseSoaReply(Environment* env,
   }
   ptr += temp_len + NS_QFIXEDSZ;
 
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // nsname
+      Undefined(env->isolate()),  // hostmaster
+      Undefined(env->isolate()),  // serial
+      Undefined(env->isolate()),  // refresh
+      Undefined(env->isolate()),  // retry
+      Undefined(env->isolate()),  // expire
+      Undefined(env->isolate()),  // minttl
+      Undefined(env->isolate()),  // type
+  };
+
   for (unsigned int i = 0; i < ancount; i++) {
     char* rr_name_temp = nullptr;
     long rr_temp_len;  // NOLINT(runtime/int)
@@ -734,45 +787,17 @@ Maybe<int> ParseSoaReply(Environment* env,
       const unsigned int expire = nbytes::ReadUint32BE(ptr + 3 * 4);
       const unsigned int minttl = nbytes::ReadUint32BE(ptr + 4 * 4);
 
-      Local<Object> soa_record = Object::New(env->isolate());
-      if (soa_record
-              ->Set(env->context(),
-                    env->nsname_string(),
-                    OneByteString(env->isolate(), nsname.get()))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->hostmaster_string(),
-                    OneByteString(env->isolate(), hostmaster.get()))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->serial_string(),
-                    Integer::NewFromUnsigned(env->isolate(), serial))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->refresh_string(),
-                    Integer::New(env->isolate(), refresh))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->retry_string(),
-                    Integer::New(env->isolate(), retry))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->expire_string(),
-                    Integer::New(env->isolate(), expire))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(),
-                    env->minttl_string(),
-                    Integer::NewFromUnsigned(env->isolate(), minttl))
-              .IsNothing() ||
-          soa_record
-              ->Set(env->context(), env->type_string(), env->dns_soa_string())
-              .IsNothing()) {
+      values[0] = OneByteString(env->isolate(), nsname.get());
+      values[1] = OneByteString(env->isolate(), hostmaster.get());
+      values[2] = Integer::NewFromUnsigned(env->isolate(), serial);
+      values[3] = Integer::New(env->isolate(), refresh);
+      values[4] = Integer::New(env->isolate(), retry);
+      values[5] = Integer::New(env->isolate(), expire);
+      values[6] = Integer::NewFromUnsigned(env->isolate(), minttl);
+      values[7] = env->dns_soa_string();
+      Local<Object> soa_record;
+      if (!NewDictionaryInstance(env->context(), tmpl, values)
+               .ToLocal(&soa_record)) {
         return Nothing<int>();
       }
 
@@ -1083,30 +1108,61 @@ Maybe<int> AnyTraits::Parse(QueryAnyWrap* wrap,
 
   if (type == ns_t_a) {
     CHECK_EQ(static_cast<uint32_t>(naddrttls), a_count);
+
+    auto tmpl = env->a_record_template();
+    if (tmpl.IsEmpty()) {
+      static constexpr std::string_view names[] = {
+          "address",
+          "ttl",
+          "type",
+      };
+      tmpl = DictionaryTemplate::New(env->isolate(), names);
+      env->set_a_record_template(tmpl);
+    }
+    MaybeLocal<Value> values[] = {
+        Undefined(env->isolate()),  // address
+        Undefined(env->isolate()),  // ttl
+        Undefined(env->isolate()),  // type
+    };
+
     for (uint32_t i = 0; i < a_count; i++) {
-      Local<Object> obj = Object::New(env->isolate());
       Local<Value> address;
-      if (!ret->Get(env->context(), i).ToLocal(&address) ||
-          obj->Set(env->context(), env->address_string(), address)
-              .IsNothing() ||
-          obj->Set(env->context(),
-                   env->ttl_string(),
-                   Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl))
-              .IsNothing() ||
-          obj->Set(env->context(), env->type_string(), env->dns_a_string())
-              .IsNothing() ||
+      if (!ret->Get(env->context(), i).ToLocal(&address)) {
+        return Nothing<int>();
+      }
+      values[0] = address;
+      values[1] = Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl);
+      values[2] = env->dns_a_string();
+
+      Local<Object> obj;
+      if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) ||
           ret->Set(env->context(), i, obj).IsNothing()) {
         return Nothing<int>();
       }
     }
   } else {
+    auto tmpl = env->cname_record_template();
+    if (tmpl.IsEmpty()) {
+      static constexpr std::string_view names[] = {
+          "value",
+          "type",
+      };
+      tmpl = DictionaryTemplate::New(env->isolate(), names);
+      env->set_cname_record_template(tmpl);
+    }
+    MaybeLocal<Value> values[] = {
+        Undefined(env->isolate()),  // value
+        Undefined(env->isolate()),  // type
+    };
     for (uint32_t i = 0; i < a_count; i++) {
-      Local<Object> obj = Object::New(env->isolate());
       Local<Value> value;
-      if (!ret->Get(env->context(), i).ToLocal(&value) ||
-          obj->Set(env->context(), env->value_string(), value).IsNothing() ||
-          obj->Set(env->context(), env->type_string(), env->dns_cname_string())
-              .IsNothing() ||
+      if (!ret->Get(env->context(), i).ToLocal(&value)) {
+        return Nothing<int>();
+      }
+      values[0] = value;
+      values[1] = env->dns_cname_string();
+      Local<Object> obj;
+      if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) ||
           ret->Set(env->context(), i, obj).IsNothing()) {
         return Nothing<int>();
       }
@@ -1128,19 +1184,35 @@ Maybe<int> AnyTraits::Parse(QueryAnyWrap* wrap,
 
   CHECK_EQ(aaaa_count, static_cast<uint32_t>(naddr6ttls));
   CHECK_EQ(ret->Length(), a_count + aaaa_count);
+
+  auto tmpl = env->aaaa_record_template();
+  if (tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "address",
+        "ttl",
+        "type",
+    };
+    tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_aaaa_record_template(tmpl);
+  }
+
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // address
+      Undefined(env->isolate()),  // ttl
+      Undefined(env->isolate()),  // type
+  };
+
   for (uint32_t i = a_count; i < ret->Length(); i++) {
-    Local<Object> obj = Object::New(env->isolate());
     Local<Value> address;
-
-    if (!ret->Get(env->context(), i).ToLocal(&address) ||
-        obj->Set(env->context(), env->address_string(), address).IsNothing() ||
-        obj->Set(env->context(),
-                 env->ttl_string(),
-                 Integer::NewFromUnsigned(env->isolate(),
-                                          addr6ttls[i - a_count].ttl))
-            .IsNothing() ||
-        obj->Set(env->context(), env->type_string(), env->dns_aaaa_string())
-            .IsNothing() ||
+    if (!ret->Get(env->context(), i).ToLocal(&address)) {
+      return Nothing<int>();
+    }
+    values[0] = address;
+    values[1] =
+        Integer::NewFromUnsigned(env->isolate(), addr6ttls[i - a_count].ttl);
+    values[2] = env->dns_aaaa_string();
+    Local<Object> obj;
+    if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) ||
         ret->Set(env->context(), i, obj).IsNothing()) {
       return Nothing<int>();
     }
@@ -1164,14 +1236,31 @@ Maybe<int> AnyTraits::Parse(QueryAnyWrap* wrap,
     return Just<int>(status);
   }
 
+  auto dns_ns_tmpl = env->dns_ns_record_template();
+  if (dns_ns_tmpl.IsEmpty()) {
+    static constexpr std::string_view names[] = {
+        "value",
+        "type",
+    };
+    dns_ns_tmpl = DictionaryTemplate::New(env->isolate(), names);
+    env->set_dns_ns_record_template(dns_ns_tmpl);
+  }
+
+  MaybeLocal<Value> values_ns[] = {
+      Undefined(env->isolate()),  // value
+      Undefined(env->isolate()),  // type
+  };
+
   for (uint32_t i = old_count; i < ret->Length(); i++) {
-    Local<Object> obj = Object::New(env->isolate());
     Local<Value> value;
-
-    if (!ret->Get(env->context(), i).ToLocal(&value) ||
-        obj->Set(env->context(), env->value_string(), value).IsNothing() ||
-        obj->Set(env->context(), env->type_string(), env->dns_ns_string())
-            .IsNothing() ||
+    if (!ret->Get(env->context(), i).ToLocal(&value)) {
+      return Nothing<int>();
+    }
+    values_ns[0] = value;
+    values_ns[1] = env->dns_ns_string();
+    Local<Object> obj;
+    if (!NewDictionaryInstance(env->context(), dns_ns_tmpl, values_ns)
+             .ToLocal(&obj) ||
         ret->Set(env->context(), i, obj).IsNothing()) {
       return Nothing<int>();
     }
@@ -1201,14 +1290,17 @@ Maybe<int> AnyTraits::Parse(QueryAnyWrap* wrap,
   }
   if (status != ARES_SUCCESS && status != ARES_ENODATA)
     return Just<int>(status);
+
   for (uint32_t i = old_count; i < ret->Length(); i++) {
-    Local<Object> obj = Object::New(env->isolate());
     Local<Value> value;
-
-    if (!ret->Get(env->context(), i).ToLocal(&value) ||
-        obj->Set(env->context(), env->value_string(), value).IsNothing() ||
-        obj->Set(env->context(), env->type_string(), env->dns_ptr_string())
-            .IsNothing() ||
+    if (!ret->Get(env->context(), i).ToLocal(&value)) {
+      return Nothing<int>();
+    }
+    values_ns[0] = value;
+    values_ns[1] = env->dns_ptr_string();
+    Local<Object> obj;
+    if (!NewDictionaryInstance(env->context(), dns_ns_tmpl, values_ns)
+             .ToLocal(&obj) ||
         ret->Set(env->context(), i, obj).IsNothing()) {
       return Nothing<int>();
     }
@@ -1568,50 +1660,35 @@ Maybe<int> SoaTraits::Parse(QuerySoaWrap* wrap,
   HandleScope handle_scope(env->isolate());
   Context::Scope context_scope(env->context());
 
+  auto tmpl = getSoaRecordTemplate(env);
+  MaybeLocal<Value> values[] = {
+      Undefined(env->isolate()),  // nsname
+      Undefined(env->isolate()),  // hostmaster
+      Undefined(env->isolate()),  // serial
+      Undefined(env->isolate()),  // refresh
+      Undefined(env->isolate()),  // retry
+      Undefined(env->isolate()),  // expire
+      Undefined(env->isolate()),  // minttl
+      Undefined(env->isolate()),  // type
+  };
+
   ares_soa_reply* soa_out;
   int status = ares_parse_soa_reply(buf, len, &soa_out);
 
   if (status != ARES_SUCCESS) return Just<int>(status);
 
   auto cleanup = OnScopeLeave([&]() { ares_free_data(soa_out); });
 
-  Local<Object> soa_record = Object::New(env->isolate());
-
-  if (soa_record
-          ->Set(env->context(),
-                env->nsname_string(),
-                OneByteString(env->isolate(), soa_out->nsname))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->hostmaster_string(),
-                OneByteString(env->isolate(), soa_out->hostmaster))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->serial_string(),
-                Integer::NewFromUnsigned(env->isolate(), soa_out->serial))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->refresh_string(),
-                Integer::New(env->isolate(), soa_out->refresh))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->retry_string(),
-                Integer::New(env->isolate(), soa_out->retry))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->expire_string(),
-                Integer::New(env->isolate(), soa_out->expire))
-          .IsNothing() ||
-      soa_record
-          ->Set(env->context(),
-                env->minttl_string(),
-                Integer::NewFromUnsigned(env->isolate(), soa_out->minttl))
-          .IsNothing()) {
+  values[0] = OneByteString(env->isolate(), soa_out->nsname);
+  values[1] = OneByteString(env->isolate(), soa_out->hostmaster);
+  values[2] = Integer::NewFromUnsigned(env->isolate(), soa_out->serial);
+  values[3] = Integer::New(env->isolate(), soa_out->refresh);
+  values[4] = Integer::New(env->isolate(), soa_out->retry);
+  values[5] = Integer::New(env->isolate(), soa_out->expire);
+  values[6] = Integer::NewFromUnsigned(env->isolate(), soa_out->minttl);
+  Local<Object> soa_record;
+  if (!NewDictionaryInstance(env->context(), tmpl, values)
+           .ToLocal(&soa_record)) {
     return Nothing<int>();
   }
 

filename: src/env_properties.h
patch: @@ -89,7 +89,6 @@
   V(cached_data_produced_string, "cachedDataProduced")                         \
   V(cached_data_rejected_string, "cachedDataRejected")                         \
   V(cached_data_string, "cachedData")                                          \
-  V(cert_usage_string, "certUsage")                                            \
   V(change_string, "change")                                                   \
   V(changes_string, "changes")                                                 \
   V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite")           \
@@ -142,7 +141,6 @@
   V(dns_a_string, "A")                                                         \
   V(dns_aaaa_string, "AAAA")                                                   \
   V(dns_caa_string, "CAA")                                                     \
-  V(dns_critical_string, "critical")                                           \
   V(dns_cname_string, "CNAME")                                                 \
   V(dns_mx_string, "MX")                                                       \
   V(dns_naptr_string, "NAPTR")                                                 \
@@ -158,7 +156,6 @@
   V(emit_string, "emit")                                                       \
   V(emit_warning_string, "emitWarning")                                        \
   V(encoding_string, "encoding")                                               \
-  V(entries_string, "entries")                                                 \
   V(env_pairs_string, "envPairs")                                              \
   V(env_var_settings_string, "envVarSettings")                                 \
   V(err_sqlite_error_string, "ERR_SQLITE_ERROR")                               \
@@ -168,8 +165,6 @@
   V(errstr_string, "errstr")                                                   \
   V(events_waiting, "eventsWaiting")                                           \
   V(events, "events")                                                          \
-  V(exchange_string, "exchange")                                               \
-  V(expire_string, "expire")                                                   \
   V(exponent_string, "exponent")                                               \
   V(exports_string, "exports")                                                 \
   V(external_stream_string, "_externalStream")                                 \
@@ -200,7 +195,6 @@
   V(help_text_string, "helpText")                                              \
   V(homedir_string, "homedir")                                                 \
   V(host_string, "host")                                                       \
-  V(hostmaster_string, "hostmaster")                                           \
   V(hostname_string, "hostname")                                               \
   V(href_string, "href")                                                       \
   V(http_1_1_string, "http/1.1")                                               \
@@ -222,10 +216,8 @@
   V(jwk_d_string, "d")                                                         \
   V(jwk_dp_string, "dp")                                                       \
   V(jwk_dq_string, "dq")                                                       \
-  V(jwk_dsa_string, "DSA")                                                     \
   V(jwk_e_string, "e")                                                         \
   V(jwk_ec_string, "EC")                                                       \
-  V(jwk_g_string, "g")                                                         \
   V(jwk_k_string, "k")                                                         \
   V(jwk_kty_string, "kty")                                                     \
   V(jwk_n_string, "n")                                                         \
@@ -245,24 +237,20 @@
   V(length_string, "length")                                                   \
   V(library_string, "library")                                                 \
   V(loop_count, "loopCount")                                                   \
-  V(mac_string, "mac")                                                         \
-  V(match_string, "match")                                                     \
   V(max_buffer_string, "maxBuffer")                                            \
   V(max_concurrent_streams_string, "maxConcurrentStreams")                     \
   V(message_port_constructor_string, "MessagePort")                            \
   V(message_port_string, "messagePort")                                        \
   V(message_string, "message")                                                 \
   V(messageerror_string, "messageerror")                                       \
   V(mgf1_hash_algorithm_string, "mgf1HashAlgorithm")                           \
-  V(minttl_string, "minttl")                                                   \
   V(mode_string, "mode")                                                       \
   V(module_string, "module")                                                   \
   V(modulus_length_string, "modulusLength")                                    \
   V(name_string, "name")                                                       \
   V(named_curve_string, "namedCurve")                                          \
   V(next_string, "next")                                                       \
   V(node_string, "node")                                                       \
-  V(nsname_string, "nsname")                                                   \
   V(object_string, "Object")                                                   \
   V(ocsp_request_string, "OCSPRequest")                                        \
   V(oncertcb_string, "oncertcb")                                               \
@@ -289,7 +277,6 @@
   V(ongracefulclosecomplete_string, "ongracefulclosecomplete")                 \
   V(openssl_error_stack, "opensslErrorStack")                                  \
   V(options_string, "options")                                                 \
-  V(order_string, "order")                                                     \
   V(original_string, "original")                                               \
   V(output_string, "output")                                                   \
   V(overlapped_string, "overlapped")                                           \
@@ -309,9 +296,7 @@
   V(port1_string, "port1")                                                     \
   V(port2_string, "port2")                                                     \
   V(port_string, "port")                                                       \
-  V(preference_string, "preference")                                           \
   V(primordials_string, "primordials")                                         \
-  V(priority_string, "priority")                                               \
   V(process_string, "process")                                                 \
   V(progress_string, "progress")                                               \
   V(promise_string, "promise")                                                 \
@@ -320,16 +305,12 @@
   V(psk_string, "psk")                                                         \
   V(public_exponent_string, "publicExponent")                                  \
   V(rate_string, "rate")                                                       \
-  V(raw_string, "raw")                                                         \
   V(read_host_object_string, "_readHostObject")                                \
   V(readable_string, "readable")                                               \
   V(read_bigints_string, "readBigInts")                                        \
   V(reason_string, "reason")                                                   \
-  V(refresh_string, "refresh")                                                 \
-  V(regexp_string, "regexp")                                                   \
   V(remaining_pages_string, "remainingPages")                                  \
   V(rename_string, "rename")                                                   \
-  V(replacement_string, "replacement")                                         \
   V(required_module_facade_url_string,                                         \
     "node:internal/require_module_default_facade")                             \
   V(required_module_facade_source_string,                                      \
@@ -338,14 +319,10 @@
   V(require_string, "require")                                                 \
   V(resource_string, "resource")                                               \
   V(result_string, "result")                                                   \
-  V(retry_string, "retry")                                                     \
   V(return_arrays_string, "returnArrays")                                      \
   V(salt_length_string, "saltLength")                                          \
   V(search_string, "search")                                                   \
-  V(selector_string, "selector")                                               \
-  V(serial_string, "serial")                                                   \
   V(servername_string, "servername")                                           \
-  V(service_string, "service")                                                 \
   V(session_id_string, "sessionId")                                            \
   V(set_string, "set")                                                         \
   V(shell_string, "shell")                                                     \
@@ -359,7 +336,6 @@
   V(source_url_string, "sourceURL")                                            \
   V(specifier_string, "specifier")                                             \
   V(stack_string, "stack")                                                     \
-  V(standard_name_string, "standardName")                                      \
   V(start_string, "start")                                                     \
   V(state_string, "state")                                                     \
   V(stats_string, "stats")                                                     \
@@ -393,7 +369,6 @@
   V(value_string, "value")                                                     \
   V(verify_error_string, "verifyError")                                        \
   V(version_string, "version")                                                 \
-  V(weight_string, "weight")                                                   \
   V(windows_hide_string, "windowsHide")                                        \
   V(windows_verbatim_arguments_string, "windowsVerbatimArguments")             \
   V(wrap_string, "wrap")                                                       \
@@ -402,13 +377,17 @@
   V(write_queue_size_string, "writeQueueSize")
 
 #define PER_ISOLATE_TEMPLATE_PROPERTIES(V)                                     \
+  V(a_record_template, v8::DictionaryTemplate)                                 \
+  V(aaaa_record_template, v8::DictionaryTemplate)                              \
   V(async_wrap_ctor_template, v8::FunctionTemplate)                            \
   V(binding_data_default_template, v8::ObjectTemplate)                         \
   V(blob_constructor_template, v8::FunctionTemplate)                           \
   V(blob_reader_constructor_template, v8::FunctionTemplate)                    \
   V(blocklist_constructor_template, v8::FunctionTemplate)                      \
+  V(caa_record_template, v8::DictionaryTemplate)                               \
   V(callsite_template, v8::DictionaryTemplate)                                 \
   V(cipherinfo_template, v8::DictionaryTemplate)                               \
+  V(cname_record_template, v8::DictionaryTemplate)                             \
   V(contextify_global_template, v8::ObjectTemplate)                            \
   V(contextify_wrapper_template, v8::ObjectTemplate)                           \
   V(cpu_usage_template, v8::DictionaryTemplate)                                \
@@ -417,6 +396,7 @@
   V(env_proxy_ctor_template, v8::FunctionTemplate)                             \
   V(ephemeral_key_template, v8::DictionaryTemplate)                            \
   V(dir_instance_template, v8::ObjectTemplate)                                 \
+  V(dns_ns_record_template, v8::DictionaryTemplate)                            \
   V(fd_constructor_template, v8::ObjectTemplate)                               \
   V(fdclose_constructor_template, v8::ObjectTemplate)                          \
   V(filehandlereadwrap_template, v8::ObjectTemplate)                           \
@@ -437,21 +417,27 @@
   V(lock_holder_constructor_template, v8::FunctionTemplate)                    \
   V(message_port_constructor_template, v8::FunctionTemplate)                   \
   V(module_wrap_constructor_template, v8::FunctionTemplate)                    \
+  V(mx_record_template, v8::DictionaryTemplate)                                \
+  V(naptr_record_template, v8::DictionaryTemplate)                             \
   V(object_stats_template, v8::DictionaryTemplate)                             \
   V(page_stats_template, v8::DictionaryTemplate)                               \
   V(pipe_constructor_template, v8::FunctionTemplate)                           \
   V(script_context_constructor_template, v8::FunctionTemplate)                 \
   V(secure_context_constructor_template, v8::FunctionTemplate)                 \
   V(shutdown_wrap_template, v8::ObjectTemplate)                                \
+  V(soa_record_template, v8::DictionaryTemplate)                               \
   V(socketaddress_constructor_template, v8::FunctionTemplate)                  \
   V(space_stats_template, v8::DictionaryTemplate)                              \
   V(sqlite_column_template, v8::DictionaryTemplate)                            \
   V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate)          \
   V(sqlite_statement_sync_iterator_constructor_template, v8::FunctionTemplate) \
   V(sqlite_session_constructor_template, v8::FunctionTemplate)                 \
+  V(srv_record_template, v8::DictionaryTemplate)                               \
   V(streambaseoutputstream_constructor_template, v8::ObjectTemplate)           \
   V(tcp_constructor_template, v8::FunctionTemplate)                            \
+  V(tlsa_record_template, v8::DictionaryTemplate)                              \
   V(tty_constructor_template, v8::FunctionTemplate)                            \
+  V(txt_record_template, v8::DictionaryTemplate)                               \
   V(urlpatterncomponentresult_template, v8::DictionaryTemplate)                \
   V(urlpatterninit_template, v8::DictionaryTemplate)                           \
   V(urlpatternresult_template, v8::DictionaryTemplate)                         \