From 866b1e67cc31f6a097b8161056316dd329d3e68e Mon Sep 17 00:00:00 2001
From: Antoine du Hamel <duhamelantoine1995@gmail.com>
Date: Sun, 15 Nov 2020 20:59:39 +0100
Subject: [PATCH] quic: refactor to use more primordials

PR-URL: https://github.com/nodejs/node/pull/36211
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
---
 lib/internal/quic/core.js | 156 +++++++++++++++++++++++---------------
 1 file changed, 93 insertions(+), 63 deletions(-)

diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js
index 98a2861e1a76fe..d6f94ed831a982 100644
--- a/lib/internal/quic/core.js
+++ b/lib/internal/quic/core.js
@@ -11,16 +11,23 @@ assertCrypto();
 
 const {
   ArrayFrom,
+  ArrayPrototypePush,
   BigInt64Array,
   Boolean,
   Error,
+  FunctionPrototypeBind,
+  FunctionPrototypeCall,
   Map,
   Number,
   Promise,
   PromiseAll,
+  PromisePrototypeThen,
+  PromisePrototypeCatch,
+  PromisePrototypeFinally,
   PromiseReject,
   PromiseResolve,
-  Set,
+  ReflectApply,
+  SafeSet,
   Symbol,
   SymbolFor,
 } = primordials;
@@ -302,13 +309,15 @@ function onSessionClose(code, family, silent, statelessReset) {
 // being requested. It is only called if the 'clientHelloHandler' option is
 // specified on listen().
 function onSessionClientHello(alpn, servername, ciphers) {
-  this[owner_symbol][kClientHello](alpn, servername, ciphers)
-    .then((context) => {
+  PromisePrototypeThen(
+    this[owner_symbol][kClientHello](alpn, servername, ciphers),
+    (context) => {
       if (context !== undefined && !context?.context)
         throw new ERR_INVALID_ARG_TYPE('context', 'SecureContext', context);
       this.onClientHelloDone(context?.context);
-    })
-    .catch((error) => this[owner_symbol].destroy(error));
+    },
+    (error) => this[owner_symbol].destroy(error)
+  );
 }
 
 // This callback is only ever invoked for QuicServerSession instances,
@@ -316,8 +325,9 @@ function onSessionClientHello(alpn, servername, ciphers) {
 // user callback must invoke .onCertDone() in order for the
 // TLS handshake to continue.
 function onSessionCert(servername) {
-  this[owner_symbol][kHandleOcsp](servername)
-    .then((data) => {
+  PromisePrototypeThen(
+    this[owner_symbol][kHandleOcsp](servername),
+    (data) => {
       if (data !== undefined) {
         if (typeof data === 'string')
           data = Buffer.from(data);
@@ -329,8 +339,9 @@ function onSessionCert(servername) {
         }
       }
       this.onCertDone(data);
-    })
-    .catch((error) => this[owner_symbol].destroy(error));
+    },
+    (error) => this[owner_symbol].destroy(error)
+  );
 }
 
 // This callback is only ever invoked for QuicClientSession instances,
@@ -338,8 +349,10 @@ function onSessionCert(servername) {
 // If the requestOCSP configuration option is false, this will never
 // be called.
 function onSessionStatus(data) {
-  this[owner_symbol][kHandleOcsp](data)
-    .catch((error) => this[owner_symbol].destroy(error));
+  PromisePrototypeCatch(
+    this[owner_symbol][kHandleOcsp](data),
+    (error) => this[owner_symbol].destroy(error)
+  );
 }
 
 // Called by the C++ internals when the TLS handshake is completed.
@@ -369,12 +382,13 @@ function onSessionHandshake(
 // resumption and 0RTT.
 function onSessionTicket(sessionTicket, transportParams) {
   if (this[owner_symbol]) {
-    process.nextTick(
-      emit.bind(
-        this[owner_symbol],
-        'sessionTicket',
-        sessionTicket,
-        transportParams));
+    process.nextTick(FunctionPrototypeBind(
+      emit,
+      this[owner_symbol],
+      'sessionTicket',
+      sessionTicket,
+      transportParams
+    ));
   }
 }
 
@@ -384,13 +398,14 @@ function onSessionTicket(sessionTicket, transportParams) {
 function onSessionPathValidation(res, local, remote) {
   const session = this[owner_symbol];
   if (session) {
-    process.nextTick(
-      emit.bind(
-        session,
-        'pathValidation',
-        res === NGTCP2_PATH_VALIDATION_RESULT_FAILURE ? 'failure' : 'success',
-        local,
-        remote));
+    process.nextTick(FunctionPrototypeBind(
+      emit,
+      session,
+      'pathValidation',
+      res === NGTCP2_PATH_VALIDATION_RESULT_FAILURE ? 'failure' : 'success',
+      local,
+      remote
+    ));
   }
 }
 
@@ -486,7 +501,7 @@ function onStreamHeaders(id, headers, kind, push_id) {
 // When a stream is flow control blocked, causes a blocked event
 // to be emitted. This is a purely informational event.
 function onStreamBlocked() {
-  process.nextTick(emit.bind(this[owner_symbol], 'blocked'));
+  process.nextTick(FunctionPrototypeBind(emit, this[owner_symbol], 'blocked'));
 }
 
 // Register the callbacks with the QUIC internal binding.
@@ -543,14 +558,17 @@ function addressOrLocalhost(address, type) {
 }
 
 function deferredClosePromise(state) {
-  return state.closePromise = new Promise((resolve, reject) => {
-    state.closePromiseResolve = resolve;
-    state.closePromiseReject = reject;
-  }).finally(() => {
-    state.closePromise = undefined;
-    state.closePromiseResolve = undefined;
-    state.closePromiseReject = undefined;
-  });
+  return state.closePromise = PromisePrototypeFinally(
+    new Promise((resolve, reject) => {
+      state.closePromiseResolve = resolve;
+      state.closePromiseReject = reject;
+    }),
+    () => {
+      state.closePromise = undefined;
+      state.closePromiseResolve = undefined;
+      state.closePromiseReject = undefined;
+    }
+  );
 }
 
 async function resolvePreferredAddress(lookup, preferredAddress) {
@@ -640,7 +658,7 @@ class QuicEndpoint {
     if (state.bindPromise !== undefined)
       return state.bindPromise;
 
-    return state.bindPromise = this[kBind]().finally(() => {
+    return state.bindPromise = PromisePrototypeFinally(this[kBind](), () => {
       state.bindPromise = undefined;
     });
   }
@@ -899,7 +917,7 @@ class QuicSocket extends EventEmitter {
     closePromiseResolve: undefined,
     closePromiseReject: undefined,
     defaultEncoding: undefined,
-    endpoints: new Set(),
+    endpoints: new SafeSet(),
     highWaterMark: undefined,
     listenPending: false,
     listenPromise: undefined,
@@ -908,7 +926,7 @@ class QuicSocket extends EventEmitter {
     clientHelloHandler: undefined,
     server: undefined,
     serverSecureContext: undefined,
-    sessions: new Set(),
+    sessions: new SafeSet(),
     state: kSocketUnbound,
     sharedState: undefined,
     stats: undefined,
@@ -1048,9 +1066,12 @@ class QuicSocket extends EventEmitter {
     if (state.bindPromise !== undefined)
       return state.bindPromise;
 
-    return state.bindPromise = this[kBind](options).finally(() => {
-      state.bindPromise = undefined;
-    });
+    return state.bindPromise = PromisePrototypeFinally(
+      this[kBind](options),
+      () => {
+        state.bindPromise = undefined;
+      }
+    );
   }
 
   async [kBind](options) {
@@ -1074,7 +1095,7 @@ class QuicSocket extends EventEmitter {
 
     const binds = [];
     for (const endpoint of state.endpoints)
-      binds.push(endpoint.bind({ signal }));
+      ArrayPrototypePush(binds, endpoint.bind({ signal }));
 
     await PromiseAll(binds);
 
@@ -1169,9 +1190,12 @@ class QuicSocket extends EventEmitter {
     if (state.listenPromise !== undefined)
       return state.listenPromise;
 
-    return state.listenPromise = this[kListen](options).finally(() => {
-      state.listenPromise = undefined;
-    });
+    return state.listenPromise = PromisePrototypeFinally(
+      this[kListen](options),
+      () => {
+        state.listenPromise = undefined;
+      }
+    );
   }
 
   async [kListen](options) {
@@ -1388,8 +1412,9 @@ class QuicSocket extends EventEmitter {
     // Otherwise, loop through each of the known sessions and close them.
     const reqs = [promise];
     for (const session of state.sessions) {
-      reqs.push(session.close()
-                       .catch((error) => this.destroy(error)));
+      ArrayPrototypePush(reqs,
+                         PromisePrototypeCatch(session.close(),
+                                               (error) => this.destroy(error)));
     }
     return PromiseAll(reqs);
   }
@@ -1441,11 +1466,11 @@ class QuicSocket extends EventEmitter {
     if (error) {
       if (typeof state.closePromiseReject === 'function')
         state.closePromiseReject(error);
-      process.nextTick(emit.bind(this, 'error', error));
+      process.nextTick(FunctionPrototypeBind(emit, this, 'error', error));
     } else if (typeof state.closePromiseResolve === 'function') {
       state.closePromiseResolve();
     }
-    process.nextTick(emit.bind(this, 'close'));
+    process.nextTick(FunctionPrototypeBind(emit, this, 'close'));
   }
 
   ref() {
@@ -1714,14 +1739,17 @@ class QuicSession extends EventEmitter {
     if (state.handshakeCompletePromise !== undefined)
       return state.handshakeCompletePromise;
 
-    state.handshakeCompletePromise = new Promise((resolve, reject) => {
-      state.handshakeCompletePromiseResolve = resolve;
-      state.handshakeCompletePromiseReject = reject;
-    }).finally(() => {
-      state.handshakeCompletePromise = undefined;
-      state.handshakeCompletePromiseReject = undefined;
-      state.handshakeCompletePromiseResolve = undefined;
-    });
+    state.handshakeCompletePromise = PromisePrototypeFinally(
+      new Promise((resolve, reject) => {
+        state.handshakeCompletePromiseResolve = resolve;
+        state.handshakeCompletePromiseReject = reject;
+      }),
+      () => {
+        state.handshakeCompletePromise = undefined;
+        state.handshakeCompletePromiseReject = undefined;
+        state.handshakeCompletePromiseResolve = undefined;
+      }
+    );
 
     return state.handshakeCompletePromise;
   }
@@ -1985,7 +2013,7 @@ class QuicSession extends EventEmitter {
     if (error) {
       if (typeof state.closePromiseReject === 'function')
         state.closePromiseReject(error);
-      process.nextTick(emit.bind(this, 'error', error));
+      process.nextTick(FunctionPrototypeBind(emit, this, 'error', error));
     } else if (typeof state.closePromiseResolve === 'function')
       state.closePromiseResolve();
 
@@ -1994,7 +2022,7 @@ class QuicSession extends EventEmitter {
         new ERR_OPERATION_FAILED('Handshake failed'));
     }
 
-    process.nextTick(emit.bind(this, 'close'));
+    process.nextTick(FunctionPrototypeBind(emit, this, 'close'));
   }
 
   // For server QuicSession instances, true if earlyData is
@@ -2698,7 +2726,7 @@ class QuicStream extends Duplex {
       default:
         assert.fail('Invalid headers kind');
     }
-    process.nextTick(emit.bind(this, name, headers, push_id));
+    process.nextTick(FunctionPrototypeBind(emit, this, name, headers, push_id));
   }
 
   [kAfterAsyncWrite]({ bytes }) {
@@ -2809,7 +2837,7 @@ class QuicStream extends Duplex {
     if (!this.destroyed) {
       if (!this.detached)
         this[kInternalState].sharedState.writeEnded = true;
-      super.end.apply(this, args);
+      ReflectApply(super.end, this, args);
     }
     return this;
   }
@@ -2825,13 +2853,14 @@ class QuicStream extends Duplex {
       state.didRead = true;
     }
 
-    streamOnResume.call(this);
+    FunctionPrototypeCall(streamOnResume, this);
   }
 
   sendFile(path, options = {}) {
     if (this.detached)
       throw new ERR_INVALID_STATE('Unable to send file');
-    fs.open(path, 'r', QuicStream[kOnFileOpened].bind(this, options));
+    fs.open(path, 'r',
+            FunctionPrototypeBind(QuicStream[kOnFileOpened], this, options));
   }
 
   static [kOnFileOpened](options, err, fd) {
@@ -2847,7 +2876,7 @@ class QuicStream extends Duplex {
     }
 
     if (this.destroyed || this.closed) {
-      fs.close(fd, (err) => { if (err) throw err; });
+      fs.close(fd, assert.ifError);
       return;
     }
 
@@ -2895,7 +2924,8 @@ class QuicStream extends Duplex {
   static [kOnFileUnpipe]() {  // Called on the StreamPipe instance.
     const stream = this.sink[owner_symbol];
     if (stream.ownsFd)
-      this.source.close().catch(stream.destroy.bind(stream));
+      PromisePrototypeCatch(this.source.close(),
+                            FunctionPrototypeBind(stream.destroy, stream));
     else
       this.source.releaseFD();
     stream.end();