From f06ac1363b8ebf199eeafd68ef70242b3709ff38 Mon Sep 17 00:00:00 2001 From: "Timothy O. Peters" 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" 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" 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" 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" 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) {