Skip to content

Commit

Permalink
buffer: support boxed strings and toPrimitive
Browse files Browse the repository at this point in the history
Add support for `Buffer.from(new String('...'))` and
`Buffer.from({[Symbol.toPrimitive]() { return '...'; }})`

PR-URL: #13725
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  • Loading branch information
jasnell authored and addaleax committed Jul 18, 2017
1 parent 70f3935 commit 683f743
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 2 deletions.
38 changes: 38 additions & 0 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,44 @@ console.log(buf2.toString());

A `TypeError` will be thrown if `str` is not a string.

### Class Method: Buffer.from(object[, offsetOrEncoding[, length]])
<!-- YAML
added: REPLACEME
-->

* `object` {Object} An object supporting `Symbol.toPrimitive` or `valueOf()`
* `offsetOrEncoding` {number|string} A byte-offset or encoding, depending on
the value returned either by `object.valueOf()` or
`object[Symbol.toPrimitive]()`.
* `length` {number} A length, depending on the value returned either by
`object.valueOf()` or `object[Symbol.toPrimitive]()`.

For objects whose `valueOf()` function returns a value not strictly equal to
`object`, returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)`.

For example:

```js
const buf = Buffer.from(new String('this is a test'));
// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
```

For objects that support `Symbol.toPrimitive`, returns
`Buffer.from(object[Symbol.toPrimitive](), offsetOrEncoding, length)`.

For example:

```js
class Foo {
[Symbol.toPrimitive]() {
return 'this is a test';
}
}

const buf = Buffer.from(new Foo(), 'utf8');
// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
```

### Class Method: Buffer.isBuffer(obj)
<!-- YAML
added: v0.1.101
Expand Down
18 changes: 16 additions & 2 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,26 @@ Buffer.from = function(value, encodingOrOffset, length) {
if (isAnyArrayBuffer(value))
return fromArrayBuffer(value, encodingOrOffset, length);

if (value == null)
throw new TypeError(kFromErrorMsg);

if (typeof value === 'number')
throw new TypeError('"value" argument must not be a number');

const valueOf = value.valueOf && value.valueOf();
if (valueOf != null && valueOf !== value)
return Buffer.from(valueOf, encodingOrOffset, length);

var b = fromObject(value);
if (b)
return b;

if (typeof value === 'number')
throw new TypeError('"value" argument must not be a number');
if (typeof value[Symbol.toPrimitive] === 'function') {
return Buffer.from(value[Symbol.toPrimitive]('string'),
encodingOrOffset,
length);
}

throw new TypeError(kFromErrorMsg);
};

Expand Down
57 changes: 57 additions & 0 deletions test/parallel/test-buffer-from.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

require('../common');
const { deepStrictEqual, throws } = require('assert');
const { Buffer } = require('buffer');
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);

const err = new RegExp('^TypeError: First argument must be a string, Buffer, ' +
'ArrayBuffer, Array, or array-like object\\.$');

[
{},
new Boolean(true),
{ valueOf() { return null; } },
{ valueOf() { return undefined; } },
{ valueOf: null },
Object.create(null)
].forEach((input) => {
throws(() => Buffer.from(input), err);
});

[
new Number(true),
new MyBadPrimitive()
].forEach((input) => {
throws(() => Buffer.from(input),
/^TypeError: "value" argument must not be a number$/);
});

0 comments on commit 683f743

Please sign in to comment.