]> zoso.dev Git - buffer.git/commitdiff
Match Node.js Buffer.from() argument handling (test-buffer-from.js)
authorFeross Aboukhadijeh <feross@feross.org>
Fri, 16 Feb 2018 08:47:03 +0000 (00:47 -0800)
committerFeross Aboukhadijeh <feross@feross.org>
Fri, 16 Feb 2018 08:47:03 +0000 (00:47 -0800)
- Handle new String() and new Boolean() using valueOf()
- Use Symbol.toPrimitive() if it exists
- Be slightly stricter with accepting .length props
- Match error messages

index.js
test/node/test-buffer-from.js [new file with mode: 0644]

index 121a438c75a823c309b67f42d8158395fea24ae2..19f30673fd8befed7def96838b670d5e04ab3b17 100644 (file)
--- a/index.js
+++ b/index.js
@@ -114,9 +114,18 @@ if (typeof Symbol !== 'undefined' && Symbol.species != null &&
 Buffer.poolSize = 8192 // not used by this implementation
 
 function from (value, encodingOrOffset, length) {
-  if (typeof value === 'number') {
-    throw new TypeError(
-      'The "value" argument must not be of type number. Received type number'
+  if (typeof value === 'string') {
+    return fromString(value, encodingOrOffset)
+  }
+
+  if (ArrayBuffer.isView(value)) {
+    return fromArrayLike(value)
+  }
+
+  if (value == null) {
+    throw TypeError(
+      'The first argument must be one of type string, Buffer, ArrayBuffer, Array ' +
+      'or Array-like Object. Received type ' + (typeof value)
     )
   }
 
@@ -125,11 +134,31 @@ function from (value, encodingOrOffset, length) {
     return fromArrayBuffer(value, encodingOrOffset, length)
   }
 
-  if (typeof value === 'string') {
-    return fromString(value, encodingOrOffset)
+  if (typeof value === 'number') {
+    throw new TypeError(
+      'The "value" argument must not be of type number. Received type number'
+    )
+  }
+
+  const valueOf = value.valueOf && value.valueOf()
+  if (valueOf != null && valueOf !== value) {
+    return Buffer.from(valueOf, encodingOrOffset, length)
   }
 
-  return fromObject(value)
+  var b = fromObject(value)
+  if (b) return b
+
+  if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
+      typeof value[Symbol.toPrimitive] === 'function') {
+    return Buffer.from(
+      value[Symbol.toPrimitive]('string'), encodingOrOffset, length
+    )
+  }
+
+  throw new TypeError(
+    'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
+    'or Array-like Object. Received type ' + (typeof value)
+  )
 }
 
 /**
@@ -268,20 +297,16 @@ function fromObject (obj) {
     return buf
   }
 
-  if (obj) {
-    if (ArrayBuffer.isView(obj) || 'length' in obj) {
-      if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
-        return createBuffer(0)
-      }
-      return fromArrayLike(obj)
-    }
-
-    if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
-      return fromArrayLike(obj.data)
+  if (obj.length !== undefined) {
+    if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
+      return createBuffer(0)
     }
+    return fromArrayLike(obj)
   }
 
-  throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.')
+  if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
+    return fromArrayLike(obj.data)
+  }
 }
 
 function checked (length) {
diff --git a/test/node/test-buffer-from.js b/test/node/test-buffer-from.js
new file mode 100644 (file)
index 0000000..6857ba3
--- /dev/null
@@ -0,0 +1,68 @@
+'use strict';
+var Buffer = require('../../').Buffer;
+
+const common = require('./common');
+const { deepStrictEqual, throws } = require('assert');
+const { runInNewContext } = require('vm');
+
+const checkString = 'test';
+
+const check = Buffer.from(checkString);
+
+class MyString extends String {
+  constructor() {
+    super(checkString);
+  }
+}
+
+class MyPrimitive {
+  [Symbol.toPrimitive]() {
+    return checkString;
+  }
+}
+
+class MyBadPrimitive {
+  [Symbol.toPrimitive]() {
+    return 1;
+  }
+}
+
+deepStrictEqual(Buffer.from(new String(checkString)), check);
+deepStrictEqual(Buffer.from(new MyString()), check);
+deepStrictEqual(Buffer.from(new MyPrimitive()), check);
+deepStrictEqual(
+  Buffer.from(runInNewContext('new String(checkString)', { checkString })),
+  check
+);
+
+[
+  [{}, 'object'],
+  [new Boolean(true), 'boolean'],
+  [{ valueOf() { return null; } }, 'object'],
+  [{ valueOf() { return undefined; } }, 'object'],
+  [{ valueOf: null }, 'object'],
+  [Object.create(null), 'object']
+].forEach(([input, actualType]) => {
+  const err = common.expectsError({
+    code: 'ERR_INVALID_ARG_TYPE',
+    type: TypeError,
+    message: 'The first argument must be one of type string, Buffer, ' +
+             'ArrayBuffer, Array, or Array-like Object. Received ' +
+             `type ${actualType}`
+  });
+  throws(() => Buffer.from(input), err);
+});
+
+[
+  new Number(true),
+  new MyBadPrimitive()
+].forEach((input) => {
+  const errMsg = common.expectsError({
+    code: 'ERR_INVALID_ARG_TYPE',
+    type: TypeError,
+    message: 'The "value" argument must not be of type number. ' +
+             'Received type number'
+  });
+  throws(() => Buffer.from(input), errMsg);
+});
+