Promisify the given function.

@param {Function} orig The function to promisify.
@param {Boolean|String[]} options.variadic The variadic option informs how promisify will handle
more than one value - by default, promisify will only resolve the promise with the first value.
false    - only resolve the promise with the first value, default behavior
true     - resolve the promise with an array containing the variadic arguments
String[] - the names of the arguments to the callback, which will be used to create an object
of values.
@return {Function: Promise} The promisified function.
function promisify(orig, options) {
  if (typeof orig !== 'function') {
    throw new TypeError('promisify requires a function');
  }

  if (orig[kCustomPromisifiedSymbol]) {
    const fn = orig[kCustomPromisifiedSymbol];
    if (typeof fn !== 'function') {
      throw new TypeError('The [util.promisify.custom] property must be a function');
    }
    Object.defineProperty(fn, kCustomPromisifiedSymbol, {
      value: fn, enumerable: false, writable: false, configurable: true
    });
    return fn;
  }

  function fn() {
    const args = toArray(arguments);
    return new Promise((resolve, reject) => {
      args.push(callbackBuilder(resolve, reject, options));
      try {
        orig.apply(this, args);
      } catch (err) {
        reject(err);
      }
    });
  }

  Object.setPrototypeOf(fn, Object.getPrototypeOf(orig));

  Object.defineProperty(fn, kCustomPromisifiedSymbol, {
    value: fn, enumerable: false, writable: false, configurable: true
  });
  return Object.defineProperties(fn, getOwnPropertyDescriptors(orig));
}