summaryrefslogtreecommitdiff
path: root/lib/v8.js
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-01-26 21:38:11 -0800
committerAnna Henningsen <anna@addaleax.net>2017-03-29 05:14:55 +0200
commit1fde98bb4fa5cab0d060994768ebd055ce6fbf2c (patch)
tree9bac24c149cc8ac88c1edc30ede6ddaa952ae47e /lib/v8.js
parent6d93508369481591ba31f34bddfd95e2cc151edb (diff)
downloadandroid-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.tar.gz
android-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.tar.bz2
android-node-v8-1fde98bb4fa5cab0d060994768ebd055ce6fbf2c.zip
v8: expose new V8 serialization API
Expose the new serialization API that was added in V8 5.5 to userland. The JS API is virtually a direct copy of what V8 provides on the C++ level. This is useful Node as a possible replacement for some internals that currently use JSON, like IPC, but is likely to be useful to general userland code as well. PR-URL: https://github.com/nodejs/node/pull/11048 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'lib/v8.js')
-rw-r--r--lib/v8.js119
1 files changed, 119 insertions, 0 deletions
diff --git a/lib/v8.js b/lib/v8.js
index a11baacdc9..7790f9b334 100644
--- a/lib/v8.js
+++ b/lib/v8.js
@@ -14,7 +14,14 @@
'use strict';
+const Buffer = require('buffer').Buffer;
+
const v8binding = process.binding('v8');
+const serdesBinding = process.binding('serdes');
+const bufferBinding = process.binding('buffer');
+
+const { objectToString } = require('internal/util');
+const { FastBuffer } = require('internal/buffer');
// Properties for heap statistics buffer extraction.
const heapStatisticsBuffer =
@@ -80,3 +87,115 @@ exports.getHeapSpaceStatistics = function() {
return heapSpaceStatistics;
};
+
+/* V8 serialization API */
+
+const Serializer = exports.Serializer = serdesBinding.Serializer;
+const Deserializer = exports.Deserializer = serdesBinding.Deserializer;
+
+/* JS methods for the base objects */
+Serializer.prototype._getDataCloneError = Error;
+
+Deserializer.prototype.readRawBytes = function(length) {
+ const offset = this._readRawBytes(length);
+ // `this.buffer` can be a Buffer or a plain Uint8Array, so just calling
+ // `.slice()` doesn't work.
+ return new FastBuffer(this.buffer.buffer,
+ this.buffer.byteOffset + offset,
+ length);
+};
+
+/* Keep track of how to handle different ArrayBufferViews.
+ * The default Serializer for Node does not use the V8 methods for serializing
+ * those objects because Node's `Buffer` objects use pooled allocation in many
+ * cases, and their underlying `ArrayBuffer`s would show up in the
+ * serialization. Because a) those may contain sensitive data and the user
+ * may not be aware of that and b) they are often much larger than the `Buffer`
+ * itself, custom serialization is applied. */
+const arrayBufferViewTypes = [Int8Array, Uint8Array, Uint8ClampedArray,
+ Int16Array, Uint16Array, Int32Array, Uint32Array,
+ Float32Array, Float64Array, DataView];
+
+const arrayBufferViewTypeToIndex = new Map();
+
+{
+ const dummy = new ArrayBuffer();
+ for (const [i, ctor] of arrayBufferViewTypes.entries()) {
+ const tag = objectToString(new ctor(dummy));
+ arrayBufferViewTypeToIndex.set(tag, i);
+ }
+}
+
+const bufferConstructorIndex = arrayBufferViewTypes.push(Buffer) - 1;
+
+class DefaultSerializer extends Serializer {
+ constructor() {
+ super();
+
+ this._setTreatArrayBufferViewsAsHostObjects(true);
+ }
+
+ _writeHostObject(abView) {
+ let i = 0;
+ if (abView.constructor === Buffer) {
+ i = bufferConstructorIndex;
+ } else {
+ const tag = objectToString(abView);
+ i = arrayBufferViewTypeToIndex.get(tag);
+
+ if (i === undefined) {
+ throw this._getDataCloneError(`Unknown host object type: ${tag}`);
+ }
+ }
+ this.writeUint32(i);
+ this.writeUint32(abView.byteLength);
+ this.writeRawBytes(new Uint8Array(abView.buffer,
+ abView.byteOffset,
+ abView.byteLength));
+ }
+}
+
+exports.DefaultSerializer = DefaultSerializer;
+
+class DefaultDeserializer extends Deserializer {
+ constructor(buffer) {
+ super(buffer);
+ }
+
+ _readHostObject() {
+ const typeIndex = this.readUint32();
+ const ctor = arrayBufferViewTypes[typeIndex];
+ const byteLength = this.readUint32();
+ const byteOffset = this._readRawBytes(byteLength);
+ const BYTES_PER_ELEMENT = ctor.BYTES_PER_ELEMENT || 1;
+
+ const offset = this.buffer.byteOffset + byteOffset;
+ if (offset % BYTES_PER_ELEMENT === 0) {
+ return new ctor(this.buffer.buffer,
+ offset,
+ byteLength / BYTES_PER_ELEMENT);
+ } else {
+ // Copy to an aligned buffer first.
+ const copy = Buffer.allocUnsafe(byteLength);
+ bufferBinding.copy(this.buffer, copy, 0, offset, offset + byteLength);
+ return new ctor(copy.buffer,
+ copy.byteOffset,
+ byteLength / BYTES_PER_ELEMENT);
+ }
+ }
+}
+
+exports.DefaultDeserializer = DefaultDeserializer;
+
+exports.serialize = function serialize(value) {
+ const ser = new DefaultSerializer();
+ ser.writeHeader();
+ ser.writeValue(value);
+ return ser.releaseBuffer();
+};
+
+exports.deserialize = function deserialize(buffer) {
+ const der = new DefaultDeserializer(buffer);
+ der.readHeader();
+ return der.readValue();
+};