Skip to content

Commit 683f743

Browse files
jasnelladdaleax
authored andcommitted
buffer: support boxed strings and toPrimitive
Add support for `Buffer.from(new String('...'))` and `Buffer.from({[Symbol.toPrimitive]() { return '...'; }})` PR-URL: #13725 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 70f3935 commit 683f743

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

doc/api/buffer.md

+38
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,44 @@ console.log(buf2.toString());
908908

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

911+
### Class Method: Buffer.from(object[, offsetOrEncoding[, length]])
912+
<!-- YAML
913+
added: REPLACEME
914+
-->
915+
916+
* `object` {Object} An object supporting `Symbol.toPrimitive` or `valueOf()`
917+
* `offsetOrEncoding` {number|string} A byte-offset or encoding, depending on
918+
the value returned either by `object.valueOf()` or
919+
`object[Symbol.toPrimitive]()`.
920+
* `length` {number} A length, depending on the value returned either by
921+
`object.valueOf()` or `object[Symbol.toPrimitive]()`.
922+
923+
For objects whose `valueOf()` function returns a value not strictly equal to
924+
`object`, returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)`.
925+
926+
For example:
927+
928+
```js
929+
const buf = Buffer.from(new String('this is a test'));
930+
// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
931+
```
932+
933+
For objects that support `Symbol.toPrimitive`, returns
934+
`Buffer.from(object[Symbol.toPrimitive](), offsetOrEncoding, length)`.
935+
936+
For example:
937+
938+
```js
939+
class Foo {
940+
[Symbol.toPrimitive]() {
941+
return 'this is a test';
942+
}
943+
}
944+
945+
const buf = Buffer.from(new Foo(), 'utf8');
946+
// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
947+
```
948+
911949
### Class Method: Buffer.isBuffer(obj)
912950
<!-- YAML
913951
added: v0.1.101

lib/buffer.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,26 @@ Buffer.from = function(value, encodingOrOffset, length) {
175175
if (isAnyArrayBuffer(value))
176176
return fromArrayBuffer(value, encodingOrOffset, length);
177177

178+
if (value == null)
179+
throw new TypeError(kFromErrorMsg);
180+
181+
if (typeof value === 'number')
182+
throw new TypeError('"value" argument must not be a number');
183+
184+
const valueOf = value.valueOf && value.valueOf();
185+
if (valueOf != null && valueOf !== value)
186+
return Buffer.from(valueOf, encodingOrOffset, length);
187+
178188
var b = fromObject(value);
179189
if (b)
180190
return b;
181191

182-
if (typeof value === 'number')
183-
throw new TypeError('"value" argument must not be a number');
192+
if (typeof value[Symbol.toPrimitive] === 'function') {
193+
return Buffer.from(value[Symbol.toPrimitive]('string'),
194+
encodingOrOffset,
195+
length);
196+
}
197+
184198
throw new TypeError(kFromErrorMsg);
185199
};
186200

test/parallel/test-buffer-from.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use strict';
2+
3+
require('../common');
4+
const { deepStrictEqual, throws } = require('assert');
5+
const { Buffer } = require('buffer');
6+
const { runInNewContext } = require('vm');
7+
8+
const checkString = 'test';
9+
10+
const check = Buffer.from(checkString);
11+
12+
class MyString extends String {
13+
constructor() {
14+
super(checkString);
15+
}
16+
}
17+
18+
class MyPrimitive {
19+
[Symbol.toPrimitive]() {
20+
return checkString;
21+
}
22+
}
23+
24+
class MyBadPrimitive {
25+
[Symbol.toPrimitive]() {
26+
return 1;
27+
}
28+
}
29+
30+
deepStrictEqual(Buffer.from(new String(checkString)), check);
31+
deepStrictEqual(Buffer.from(new MyString()), check);
32+
deepStrictEqual(Buffer.from(new MyPrimitive()), check);
33+
deepStrictEqual(Buffer.from(
34+
runInNewContext('new String(checkString)', {checkString})),
35+
check);
36+
37+
const err = new RegExp('^TypeError: First argument must be a string, Buffer, ' +
38+
'ArrayBuffer, Array, or array-like object\\.$');
39+
40+
[
41+
{},
42+
new Boolean(true),
43+
{ valueOf() { return null; } },
44+
{ valueOf() { return undefined; } },
45+
{ valueOf: null },
46+
Object.create(null)
47+
].forEach((input) => {
48+
throws(() => Buffer.from(input), err);
49+
});
50+
51+
[
52+
new Number(true),
53+
new MyBadPrimitive()
54+
].forEach((input) => {
55+
throws(() => Buffer.from(input),
56+
/^TypeError: "value" argument must not be a number$/);
57+
});

0 commit comments

Comments
 (0)