From f06ac1363b8ebf199eeafd68ef70242b3709ff38 Mon Sep 17 00:00:00 2001
From: "Timothy O. Peters" <timotewpeters@gmail.com>
Date: Sun, 28 Jan 2018 19:25:56 +0100
Subject: [PATCH 1/5] Extract out encoding validation functions

---
 lib/internal/encoding.js | 55 ++++++++++++++++++----------------------
 1 file changed, 25 insertions(+), 30 deletions(-)

diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js
index 763ee42426b343..2945877b73744e 100644
--- a/lib/internal/encoding.js
+++ b/lib/internal/encoding.js
@@ -32,6 +32,17 @@ function lazyBuffer() {
   return Buffer;
 }
 
+function validateEnDecoder(obj, type) {
+  const prop = type === 'TextDecoder' ? kDecoder : kEncoder;
+  if (obj == null || obj[prop] !== true)
+    throw new errors.TypeError('ERR_INVALID_THIS', type);
+}
+
+function validateArgument(prop, expected, propName) {
+  if (typeof prop !== expected && !isArrayBufferView(prop))
+    throw new errors.Error('ERR_INVALID_ARG_TYPE', propName, expected);
+}
+
 const CONVERTER_FLAGS_FLUSH = 0x1;
 const CONVERTER_FLAGS_FATAL = 0x2;
 const CONVERTER_FLAGS_IGNORE_BOM = 0x4;
@@ -288,20 +299,17 @@ class TextEncoder {
   }
 
   get encoding() {
-    if (this == null || this[kEncoder] !== true)
-      throw new errors.TypeError('ERR_INVALID_THIS', 'TextEncoder');
+    validateEnDecoder(this, 'TextEncoder');
     return 'utf-8';
   }
 
   encode(input = '') {
-    if (this == null || this[kEncoder] !== true)
-      throw new errors.TypeError('ERR_INVALID_THIS', 'TextEncoder');
+    validateEnDecoder(this, 'TextEncoder');
     return encodeUtf8String(`${input}`);
   }
 
   [inspect](depth, opts) {
-    if (this == null || this[kEncoder] !== true)
-      throw new errors.TypeError('ERR_INVALID_THIS', 'TextEncoder');
+    validateEnDecoder(this, 'TextEncoder');
     if (typeof depth === 'number' && depth < 0)
       return opts.stylize('[Object]', 'special');
     var ctor = getConstructorOf(this);
@@ -329,8 +337,7 @@ const { hasConverter, TextDecoder } =
     makeTextDecoderJS();
 
 function hasTextDecoder(encoding = 'utf-8') {
-  if (typeof encoding !== 'string')
-    throw new errors.Error('ERR_INVALID_ARG_TYPE', 'encoding', 'string');
+  validateArgument(encoding, 'string', 'encoding');
   return hasConverter(getEncodingFromLabel(encoding));
 }
 
@@ -344,8 +351,7 @@ function makeTextDecoderICU() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      if (typeof options !== 'object')
-        throw new errors.Error('ERR_INVALID_ARG_TYPE', 'options', 'Object');
+      validateArgument(options, 'object', 'options');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined)
@@ -369,17 +375,14 @@ function makeTextDecoderICU() {
 
 
     decode(input = empty, options = {}) {
-      if (this == null || this[kDecoder] !== true)
-        throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+      validateEnDecoder(this, 'TextDecoder');
       if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
       } else if (!isArrayBufferView(input)) {
         throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
                                    ['ArrayBuffer', 'ArrayBufferView']);
       }
-      if (typeof options !== 'object') {
-        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
-      }
+      validateArgument(options, 'object', 'options');
 
       var flags = 0;
       if (options !== null)
@@ -416,8 +419,7 @@ function makeTextDecoderJS() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      if (typeof options !== 'object')
-        throw new errors.Error('ERR_INVALID_ARG_TYPE', 'options', 'Object');
+      validateArgument(options, 'object', 'options');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined || !hasConverter(enc))
@@ -440,8 +442,7 @@ function makeTextDecoderJS() {
     }
 
     decode(input = empty, options = {}) {
-      if (this == null || this[kDecoder] !== true)
-        throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+      validateEnDecoder(this, 'TextDecoder');
       if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
       } else if (isArrayBufferView(input)) {
@@ -451,9 +452,7 @@ function makeTextDecoderJS() {
         throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
                                    ['ArrayBuffer', 'ArrayBufferView']);
       }
-      if (typeof options !== 'object') {
-        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
-      }
+      validateArgument(options, 'object', 'options');
 
       if (this[kFlags] & CONVERTER_FLAGS_FLUSH) {
         this[kBOMSeen] = false;
@@ -496,27 +495,23 @@ function makeTextDecoderJS() {
     TextDecoder.prototype,
     Object.getOwnPropertyDescriptors({
       get encoding() {
-        if (this == null || this[kDecoder] !== true)
-          throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+        validateEnDecoder(this, 'TextDecoder');
         return this[kEncoding];
       },
 
       get fatal() {
-        if (this == null || this[kDecoder] !== true)
-          throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+        validateEnDecoder(this, 'TextDecoder');
         return (this[kFlags] & CONVERTER_FLAGS_FATAL) === CONVERTER_FLAGS_FATAL;
       },
 
       get ignoreBOM() {
-        if (this == null || this[kDecoder] !== true)
-          throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+        validateEnDecoder(this, 'TextDecoder');
         return (this[kFlags] & CONVERTER_FLAGS_IGNORE_BOM) ===
                CONVERTER_FLAGS_IGNORE_BOM;
       },
 
       [inspect](depth, opts) {
-        if (this == null || this[kDecoder] !== true)
-          throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
+        validateEnDecoder(this, 'TextDecoder');
         if (typeof depth === 'number' && depth < 0)
           return opts.stylize('[Object]', 'special');
         var ctor = getConstructorOf(this);

From 8f4a276fcae72fdf7a46313b7908a5880a048c78 Mon Sep 17 00:00:00 2001
From: "Timothy O. Peters" <timotewpeters@gmail.com>
Date: Mon, 29 Jan 2018 18:54:29 +0100
Subject: [PATCH 2/5] splitting out validateEncoder() and validateDecoder()

---
 lib/internal/encoding.js | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js
index 2945877b73744e..57be7418c9714f 100644
--- a/lib/internal/encoding.js
+++ b/lib/internal/encoding.js
@@ -32,10 +32,14 @@ function lazyBuffer() {
   return Buffer;
 }
 
-function validateEnDecoder(obj, type) {
-  const prop = type === 'TextDecoder' ? kDecoder : kEncoder;
-  if (obj == null || obj[prop] !== true)
-    throw new errors.TypeError('ERR_INVALID_THIS', type);
+function validateEncoder(obj) {
+  if (obj == null || obj[kEncoder] !== true)
+    throw new errors.TypeError('ERR_INVALID_THIS', 'TextEncoder');
+}
+
+function validateDecoder(obj) {
+  if (obj == null || obj[kDecoder] !== true)
+    throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
 }
 
 function validateArgument(prop, expected, propName) {
@@ -299,17 +303,17 @@ class TextEncoder {
   }
 
   get encoding() {
-    validateEnDecoder(this, 'TextEncoder');
+    validateEncoder(this);
     return 'utf-8';
   }
 
   encode(input = '') {
-    validateEnDecoder(this, 'TextEncoder');
+    validateEncoder(this);
     return encodeUtf8String(`${input}`);
   }
 
   [inspect](depth, opts) {
-    validateEnDecoder(this, 'TextEncoder');
+    validateEncoder(this);
     if (typeof depth === 'number' && depth < 0)
       return opts.stylize('[Object]', 'special');
     var ctor = getConstructorOf(this);
@@ -375,7 +379,7 @@ function makeTextDecoderICU() {
 
 
     decode(input = empty, options = {}) {
-      validateEnDecoder(this, 'TextDecoder');
+      validateDecoder(this);
       if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
       } else if (!isArrayBufferView(input)) {
@@ -442,7 +446,7 @@ function makeTextDecoderJS() {
     }
 
     decode(input = empty, options = {}) {
-      validateEnDecoder(this, 'TextDecoder');
+      validateDecoder(this);
       if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
       } else if (isArrayBufferView(input)) {
@@ -495,23 +499,23 @@ function makeTextDecoderJS() {
     TextDecoder.prototype,
     Object.getOwnPropertyDescriptors({
       get encoding() {
-        validateEnDecoder(this, 'TextDecoder');
+        validateDecoder(this);
         return this[kEncoding];
       },
 
       get fatal() {
-        validateEnDecoder(this, 'TextDecoder');
+        validateDecoder(this);
         return (this[kFlags] & CONVERTER_FLAGS_FATAL) === CONVERTER_FLAGS_FATAL;
       },
 
       get ignoreBOM() {
-        validateEnDecoder(this, 'TextDecoder');
+        validateDecoder(this);
         return (this[kFlags] & CONVERTER_FLAGS_IGNORE_BOM) ===
                CONVERTER_FLAGS_IGNORE_BOM;
       },
 
       [inspect](depth, opts) {
-        validateEnDecoder(this, 'TextDecoder');
+        validateDecoder(this);
         if (typeof depth === 'number' && depth < 0)
           return opts.stylize('[Object]', 'special');
         var ctor = getConstructorOf(this);

From b83472dc5631d5bf289144f4c6c9f0a03ea4fd6a Mon Sep 17 00:00:00 2001
From: "Timothy O. Peters" <timotewpeters@gmail.com>
Date: Wed, 31 Jan 2018 06:08:58 +0100
Subject: [PATCH 3/5] remove check for isArrayBufferView

---
 lib/internal/encoding.js | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js
index 57be7418c9714f..f25bfd8327cfad 100644
--- a/lib/internal/encoding.js
+++ b/lib/internal/encoding.js
@@ -43,7 +43,7 @@ function validateDecoder(obj) {
 }
 
 function validateArgument(prop, expected, propName) {
-  if (typeof prop !== expected && !isArrayBufferView(prop))
+  if (typeof prop !== expected.toLowerCase())
     throw new errors.Error('ERR_INVALID_ARG_TYPE', propName, expected);
 }
 
@@ -355,7 +355,7 @@ function makeTextDecoderICU() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      validateArgument(options, 'object', 'options');
+      validateArgument(options, 'Object', 'options');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined)
@@ -386,7 +386,7 @@ function makeTextDecoderICU() {
         throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
                                    ['ArrayBuffer', 'ArrayBufferView']);
       }
-      validateArgument(options, 'object', 'options');
+      validateArgument(options, 'Object', 'options');
 
       var flags = 0;
       if (options !== null)
@@ -423,7 +423,7 @@ function makeTextDecoderJS() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      validateArgument(options, 'object', 'options');
+      validateArgument(options, 'Object', 'options');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined || !hasConverter(enc))
@@ -456,7 +456,7 @@ function makeTextDecoderJS() {
         throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
                                    ['ArrayBuffer', 'ArrayBufferView']);
       }
-      validateArgument(options, 'object', 'options');
+      validateArgument(options, 'Object', 'options');
 
       if (this[kFlags] & CONVERTER_FLAGS_FLUSH) {
         this[kBOMSeen] = false;

From 9ee5f960c2d99f9078e2d1e05fc8f138b1c48f79 Mon Sep 17 00:00:00 2001
From: "Timothy O. Peters" <timotewpeters@gmail.com>
Date: Wed, 31 Jan 2018 10:56:26 +0100
Subject: [PATCH 4/5] split the buffer validation

---
 lib/internal/encoding.js | 42 ++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js
index f25bfd8327cfad..0f1c8d104dd2d3 100644
--- a/lib/internal/encoding.js
+++ b/lib/internal/encoding.js
@@ -42,9 +42,15 @@ function validateDecoder(obj) {
     throw new errors.TypeError('ERR_INVALID_THIS', 'TextDecoder');
 }
 
-function validateArgument(prop, expected, propName) {
-  if (typeof prop !== expected.toLowerCase())
-    throw new errors.Error('ERR_INVALID_ARG_TYPE', propName, expected);
+function validateArgument(prop, expected, propName, expectedName) {
+  if (typeof prop !== expected)
+    throw new errors.Error('ERR_INVALID_ARG_TYPE', propName, expectedName);
+}
+
+function validateArgumentBuffer(prop, propName) {
+  if (!isArrayBuffer(prop) || !isArrayBufferView(prop))
+    throw new errors.Error('ERR_INVALID_ARG_TYPE', propName,
+                           ['ArrayBuffer', 'ArrayBufferView']);
 }
 
 const CONVERTER_FLAGS_FLUSH = 0x1;
@@ -341,7 +347,7 @@ const { hasConverter, TextDecoder } =
     makeTextDecoderJS();
 
 function hasTextDecoder(encoding = 'utf-8') {
-  validateArgument(encoding, 'string', 'encoding');
+  validateArgument(encoding, 'string', 'encoding', 'string');
   return hasConverter(getEncodingFromLabel(encoding));
 }
 
@@ -355,7 +361,7 @@ function makeTextDecoderICU() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      validateArgument(options, 'Object', 'options');
+      validateArgument(options, 'object', 'options', 'Object');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined)
@@ -380,13 +386,12 @@ function makeTextDecoderICU() {
 
     decode(input = empty, options = {}) {
       validateDecoder(this);
-      if (isArrayBuffer(input)) {
+      validateArgumentBuffer(input, 'input');
+
+      if (isArrayBuffer(input))
         input = lazyBuffer().from(input);
-      } else if (!isArrayBufferView(input)) {
-        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
-                                   ['ArrayBuffer', 'ArrayBufferView']);
-      }
-      validateArgument(options, 'Object', 'options');
+
+      validateArgument(options, 'object', 'options', 'Object');
 
       var flags = 0;
       if (options !== null)
@@ -423,7 +428,7 @@ function makeTextDecoderJS() {
   class TextDecoder {
     constructor(encoding = 'utf-8', options = {}) {
       encoding = `${encoding}`;
-      validateArgument(options, 'Object', 'options');
+      validateArgument(options, 'object', 'options', 'Object');
 
       const enc = getEncodingFromLabel(encoding);
       if (enc === undefined || !hasConverter(enc))
@@ -447,16 +452,11 @@ function makeTextDecoderJS() {
 
     decode(input = empty, options = {}) {
       validateDecoder(this);
-      if (isArrayBuffer(input)) {
+      validateArgumentBuffer(input, 'input');
+
+      if (isArrayBuffer(input))
         input = lazyBuffer().from(input);
-      } else if (isArrayBufferView(input)) {
-        input = lazyBuffer().from(input.buffer, input.byteOffset,
-                                  input.byteLength);
-      } else {
-        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
-                                   ['ArrayBuffer', 'ArrayBufferView']);
-      }
-      validateArgument(options, 'Object', 'options');
+      validateArgument(options, 'object', 'options', 'Object');
 
       if (this[kFlags] & CONVERTER_FLAGS_FLUSH) {
         this[kBOMSeen] = false;

From 077c2584b5629a563666cfcb40ce84bd211a8653 Mon Sep 17 00:00:00 2001
From: "Timothy O. Peters" <timotewpeters@gmail.com>
Date: Wed, 31 Jan 2018 11:28:10 +0100
Subject: [PATCH 5/5] revert validate buffer

---
 lib/internal/encoding.js | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js
index 0f1c8d104dd2d3..fa178f3a8c7caa 100644
--- a/lib/internal/encoding.js
+++ b/lib/internal/encoding.js
@@ -47,12 +47,6 @@ function validateArgument(prop, expected, propName, expectedName) {
     throw new errors.Error('ERR_INVALID_ARG_TYPE', propName, expectedName);
 }
 
-function validateArgumentBuffer(prop, propName) {
-  if (!isArrayBuffer(prop) || !isArrayBufferView(prop))
-    throw new errors.Error('ERR_INVALID_ARG_TYPE', propName,
-                           ['ArrayBuffer', 'ArrayBufferView']);
-}
-
 const CONVERTER_FLAGS_FLUSH = 0x1;
 const CONVERTER_FLAGS_FATAL = 0x2;
 const CONVERTER_FLAGS_IGNORE_BOM = 0x4;
@@ -386,11 +380,12 @@ function makeTextDecoderICU() {
 
     decode(input = empty, options = {}) {
       validateDecoder(this);
-      validateArgumentBuffer(input, 'input');
-
-      if (isArrayBuffer(input))
+      if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
-
+      } else if (!isArrayBufferView(input)) {
+        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
+                                   ['ArrayBuffer', 'ArrayBufferView']);
+      }
       validateArgument(options, 'object', 'options', 'Object');
 
       var flags = 0;
@@ -452,10 +447,15 @@ function makeTextDecoderJS() {
 
     decode(input = empty, options = {}) {
       validateDecoder(this);
-      validateArgumentBuffer(input, 'input');
-
-      if (isArrayBuffer(input))
+      if (isArrayBuffer(input)) {
         input = lazyBuffer().from(input);
+      } else if (isArrayBufferView(input)) {
+        input = lazyBuffer().from(input.buffer, input.byteOffset,
+                                  input.byteLength);
+      } else {
+        throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'input',
+                                   ['ArrayBuffer', 'ArrayBufferView']);
+      }
       validateArgument(options, 'object', 'options', 'Object');
 
       if (this[kFlags] & CONVERTER_FLAGS_FLUSH) {