Skip to content

Commit

Permalink
feat: add custom inspect behavior for buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
janvennemann authored and sgtcoolguy committed Oct 3, 2019
1 parent 601105b commit 1feaf6d
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 39 deletions.
74 changes: 65 additions & 9 deletions common/Resources/ti.internal/extensions/node/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@
* a Uint8Array in any of our APIs that take a Ti.Buffer and eventually deprecating/removing Ti.Buffer.
*/

import { isInsideNodeModules } from './internal/util';
import {
customInspectSymbol,
getOwnNonIndexProperties,
isBuffer,
isInsideNodeModules,
propertyFilter
} from './internal/util';

import { inspect as utilInspect } from './internal/util/inspect';

const { ALL_PROPERTIES, ONLY_ENUMERABLE } = propertyFilter;

// https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings
const TI_CODEC_MAP = new Map();
Expand Down Expand Up @@ -51,6 +61,8 @@ const uint8DoubleArray = new Uint8Array(doubleArray.buffer);
const floatArray = new Float32Array(1);
const uint8FloatArray = new Uint8Array(floatArray.buffer);

let INSPECT_MAX_BYTES = 50;

class Buffer {
/**
* Constructs a new buffer.
Expand Down Expand Up @@ -80,17 +92,23 @@ class Buffer {

const tiBuffer = arg;
let start = encodingOrOffset;
this._tiBuffer = tiBuffer;
if (start === undefined) {
start = 0;
}
this.byteOffset = start;
if (length === undefined) {
this.length = tiBuffer.length - this.byteOffset;
} else {
this.length = length;
length = tiBuffer.length - start;
}
this._isBuffer = true;
Object.defineProperties(this, {
byteOffset: {
value: start
},
length: {
value: length
},
_tiBuffer: {
value: tiBuffer
}
});
// FIXME: Support .buffer property that holds an ArrayBuffer!
}

Expand Down Expand Up @@ -1405,10 +1423,46 @@ class Buffer {
* @returns {boolean}
*/
static isBuffer(obj) {
return obj !== null && obj !== undefined && obj._isBuffer === true;
return obj !== null && obj !== undefined && obj[isBuffer] === true;
}

// Override how buffers are presented by util.inspect().
[customInspectSymbol](recurseTimes, ctx) {
const max = INSPECT_MAX_BYTES;
const actualMax = Math.min(max, this.length);
const remaining = this.length - max;
let str = this.slice(0, actualMax).toString('hex').replace(/(.{2})/g, '$1 ').trim();
if (remaining > 0) {
str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
}
// Inspect special properties as well, if possible.
if (ctx) {
let extras = false;
const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE;
const obj = getOwnNonIndexProperties(this, filter).reduce((obj, key) => {
extras = true;
obj[key] = this[key];
return obj;
}, Object.create(null));
if (extras) {
if (this.length !== 0) {
str += ', ';
}
// '[Object: null prototype] {'.length === 26
// This is guarded with a test.
str += utilInspect(obj, {
...ctx,
breakLength: Infinity,
compact: true
}).slice(27, -2);
}
}
return `<${this.constructor.name} ${str}>`;
}
}

Buffer.prototype.inspect = Buffer.prototype[customInspectSymbol];

Buffer.poolSize = 8192;

export default {
Expand Down Expand Up @@ -1543,6 +1597,8 @@ const arrayIndexHandler = {
if (Number.isSafeInteger(num)) {
return getAdjustedIndex(target, num);
}
} else if (propKey === isBuffer) {
return true;
}
return Reflect.get(target, propKey, receiver);
},
Expand Down Expand Up @@ -1589,7 +1645,7 @@ function setAdjustedIndex(buf, index, value) {
* @returns {Buffer} wrapped inside a Proxy
*/
function newBuffer(...args) {
return new Proxy(new Buffer(...args), arrayIndexHandler); // eslint-disable-line security/detect-new-buffer
return new Proxy(new Buffer(...args), arrayIndexHandler); // eslint-disable-line security/detect-new-buffer
}

/**
Expand Down
34 changes: 34 additions & 0 deletions common/Resources/ti.internal/extensions/node/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isNativeError } from './util/types';
const kNodeModulesRE = /^(.*)[\\/]node_modules[\\/]/;

export const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');
export const isBuffer = Symbol.for('titanium.buffer.isBuffer');

const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex

Expand Down Expand Up @@ -79,3 +80,36 @@ export function uncurryThis(f) {
return f.call.apply(f, arguments);
};
}

const ALL_PROPERTIES = 0;
const ONLY_ENUMERABLE = 2;

export const propertyFilter = {
ALL_PROPERTIES,
ONLY_ENUMERABLE
};

export function getOwnNonIndexProperties(obj, filter) {
const props = [];
const keys = filter === ONLY_ENUMERABLE ? Object.keys(obj) : Object.getOwnPropertyNames(obj);
for (var i = 0; i < keys.length; ++i) {
const key = keys[i];
if (!isAllDigits(key)) {
props.push(key);
}
}
return props;
}

function isAllDigits(s) {
if (s.length === 0) {
return false;
}
for (var i = 0; i < s.length; ++i) {
const code = s.charCodeAt(i);
if (code < 48 || code > 57) {
return false;
}
}
return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ import {
import { codes, isStackOverflowError } from '../errors';
import {
customInspectSymbol,
getOwnNonIndexProperties,
isError,
join,
propertyFilter,
removeColors,
uncurryThis
} from '../util';

const { ALL_PROPERTIES, ONLY_ENUMERABLE } = propertyFilter;

const BooleanPrototype = Boolean.prototype;
const DatePrototype = Date.prototype;
const ErrorPrototype = Error.prototype;
Expand Down Expand Up @@ -482,34 +486,6 @@ function noPrototypeIterator(ctx, value, recurseTimes) {
}
}

function isAllDigits(s) {
if (s.length === 0) {
return false;
}
for (var i = 0; i < s.length; ++i) {
const code = s.charCodeAt(i);
if (code < 48 || code > 57) {
return false;
}
}
return true;
}

function getOwnNonIndexProperties(obj, filter) {
const props = [];
const keys = filter === ONLY_ENUMERABLE ? Object.keys(obj) : Object.getOwnPropertyNames(obj);
for (var i = 0; i < keys.length; ++i) {
const key = keys[i];
if (!isAllDigits(key)) {
props.push(key);
}
}
return props;
}

const ALL_PROPERTIES = 0;
const ONLY_ENUMERABLE = 2;

function formatValue(ctx, value, recurseTimes, typedArray) {
// Primitive types cannot have properties.
if (typeof value !== 'object' && typeof value !== 'function') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { uncurryThis } from '../util';
import { isBuffer, uncurryThis } from '../util';

const TypedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);

Expand Down Expand Up @@ -176,7 +176,12 @@ export function isSymbolObject(value) {
}

export function isTypedArray(value) {
return TypedArrayProto_toStringTag(value) !== undefined;
const isBuiltInTypedArray = TypedArrayProto_toStringTag(value) !== undefined;
if (isBuiltInTypedArray) {
return true;
}

return value[isBuffer] === true;
}

export function isUint8Array(value) {
Expand Down
23 changes: 23 additions & 0 deletions tests/Resources/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,29 @@ describe('util', () => {
});
});

describe('#isTypedArray()', () => {
it('should return true for built-in typed arrays', () => {
should(util.types.isTypedArray(new Uint8Array())).be.true;
should(util.types.isTypedArray(new Uint8ClampedArray())).be.true;
should(util.types.isTypedArray(new Uint16Array())).be.true;
should(util.types.isTypedArray(new Uint32Array())).be.true;
should(util.types.isTypedArray(new Int8Array())).be.true;
should(util.types.isTypedArray(new Int16Array())).be.true;
should(util.types.isTypedArray(new Int32Array())).be.true;
should(util.types.isTypedArray(new Float32Array())).be.true;
should(util.types.isTypedArray(new Float64Array())).be.true;
});

it('should return true for our own Buffer', () => {
should(util.types.isTypedArray(Buffer.alloc())).be.true;
});

it('should return false for other values', () => {
util.types.isTypedArray({}).should.be.false;
util.types.isTypedArray([]).should.be.false;
});
});

describe('Typed Arrays', () => {
it('should correctly check typed arrays', () => {
should(!util.types.isUint8Array({ [Symbol.toStringTag]: 'Uint8Array' })).be.true;
Expand Down

0 comments on commit 1feaf6d

Please sign in to comment.