This repository has been archived by the owner on Aug 31, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
worker: improve error (de)serialization
- Loading branch information
Showing
6 changed files
with
169 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
'use strict'; | ||
|
||
const Buffer = require('buffer').Buffer; | ||
const { serialize, deserialize } = require('v8'); | ||
const { SafeSet } = require('internal/safe_globals'); | ||
|
||
const kSerializedError = 0; | ||
const kSerializedObject = 1; | ||
const kInspectedError = 2; | ||
|
||
const GetPrototypeOf = Object.getPrototypeOf; | ||
const GetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | ||
const GetOwnPropertyNames = Object.getOwnPropertyNames; | ||
const DefineProperty = Object.defineProperty; | ||
const Assign = Object.assign; | ||
const ObjectPrototypeToString = | ||
Function.prototype.call.bind(Object.prototype.toString); | ||
const ForEach = Function.prototype.call.bind(Array.prototype.forEach); | ||
const Call = Function.prototype.call.bind(Function.prototype.call); | ||
|
||
const errors = { | ||
Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError | ||
}; | ||
const errorConstructorNames = new SafeSet(Object.keys(errors)); | ||
|
||
function TryGetAllProperties(object, target = object) { | ||
const all = Object.create(null); | ||
if (object === null) | ||
return all; | ||
Assign(all, TryGetAllProperties(GetPrototypeOf(object), target)); | ||
const keys = GetOwnPropertyNames(object); | ||
ForEach(keys, (key) => { | ||
const descriptor = GetOwnPropertyDescriptor(object, key); | ||
const getter = descriptor.get; | ||
if (getter && key !== '__proto__') { | ||
try { | ||
descriptor.value = Call(getter, target); | ||
} catch (e) {} | ||
} | ||
if ('value' in descriptor && typeof descriptor.value !== 'function') { | ||
delete descriptor.get; | ||
delete descriptor.set; | ||
all[key] = descriptor; | ||
} | ||
}); | ||
return all; | ||
} | ||
|
||
function GetConstructors(object) { | ||
const constructors = []; | ||
|
||
for (var current = object; | ||
current !== null; | ||
current = GetPrototypeOf(current)) { | ||
const desc = GetOwnPropertyDescriptor(current, 'constructor'); | ||
if (desc && desc.value) { | ||
DefineProperty(constructors, constructors.length, { | ||
value: desc.value, enumerable: true | ||
}); | ||
} | ||
} | ||
|
||
return constructors; | ||
} | ||
|
||
function GetName(object) { | ||
const desc = GetOwnPropertyDescriptor(object, 'name'); | ||
return desc && desc.value; | ||
} | ||
|
||
let util; | ||
function lazyUtil() { | ||
if (!util) | ||
util = require('util'); | ||
return util; | ||
} | ||
|
||
function serializeError(error) { | ||
try { | ||
if (typeof error === 'object' && | ||
ObjectPrototypeToString(error) === '[object Error]') { | ||
const constructors = GetConstructors(error); | ||
for (var i = constructors.length - 1; i >= 0; i--) { | ||
const name = GetName(constructors[i]); | ||
if (errorConstructorNames.has(name)) { | ||
try { error.stack; } catch (e) {} | ||
const serialized = serialize({ | ||
constructor: name, | ||
properties: TryGetAllProperties(error) | ||
}); | ||
return Buffer.concat([Buffer.from([kSerializedError]), serialized]); | ||
} | ||
} | ||
} | ||
} catch (e) {} | ||
try { | ||
const serialized = serialize(error); | ||
return Buffer.concat([Buffer.from([kSerializedObject]), serialized]); | ||
} catch (e) {} | ||
return Buffer.concat([Buffer.from([kInspectedError]), | ||
Buffer.from(lazyUtil().inspect(error), 'utf8')]); | ||
} | ||
|
||
function deserializeError(error) { | ||
switch (error[0]) { | ||
case kSerializedError: | ||
const { constructor, properties } = deserialize(error.slice(1)); | ||
const ctor = errors[constructor]; | ||
return Object.create(ctor.prototype, properties); | ||
case kSerializedObject: | ||
return deserialize(error.slice(1)); | ||
case kInspectedError: | ||
return error.toString('utf8', 1); | ||
} | ||
require('assert').fail('This should not happen'); | ||
} | ||
|
||
module.exports = { serializeError, deserializeError }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Flags: --expose-internals | ||
'use strict'; | ||
require('../common'); | ||
const assert = require('assert'); | ||
const errors = require('internal/errors'); | ||
const { serializeError, deserializeError } = require('internal/error-serdes'); | ||
|
||
function cycle(err) { | ||
return deserializeError(serializeError(err)); | ||
} | ||
|
||
assert.strictEqual(cycle(0), 0); | ||
assert.strictEqual(cycle(-1), -1); | ||
assert.strictEqual(cycle(1.4), 1.4); | ||
assert.strictEqual(cycle(null), null); | ||
assert.strictEqual(cycle(undefined), undefined); | ||
assert.strictEqual(cycle('foo'), 'foo'); | ||
|
||
{ | ||
const err = cycle(new Error('foo')); | ||
assert(err instanceof Error); | ||
assert.strictEqual(err.name, 'Error'); | ||
assert.strictEqual(err.message, 'foo'); | ||
assert(/^Error: foo\n/.test(err.stack)); | ||
} | ||
|
||
assert.strictEqual(cycle(new RangeError('foo')).name, 'RangeError'); | ||
assert.strictEqual(cycle(new TypeError('foo')).name, 'TypeError'); | ||
assert.strictEqual(cycle(new ReferenceError('foo')).name, 'ReferenceError'); | ||
assert.strictEqual(cycle(new URIError('foo')).name, 'URIError'); | ||
assert.strictEqual(cycle(new EvalError('foo')).name, 'EvalError'); | ||
assert.strictEqual(cycle(new SyntaxError('foo')).name, 'SyntaxError'); | ||
|
||
class SubError extends Error {} | ||
|
||
assert.strictEqual(cycle(new SubError('foo')).name, 'Error'); | ||
|
||
assert.deepStrictEqual(cycle({ message: 'foo' }), { message: 'foo' }); | ||
assert.strictEqual(cycle(Function), '[Function: Function]'); | ||
|
||
{ | ||
const err = new errors.TypeError('ERR_INVALID_ARG_TYPE', 'object', 'object'); | ||
assert(/^TypeError \[ERR_INVALID_ARG_TYPE\]:/.test(err)); | ||
assert.strictEqual(err.name, 'TypeError [ERR_INVALID_ARG_TYPE]'); | ||
assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters