Skip to content

Commit

Permalink
[Fix] handle core-js Symbol shams
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed May 8, 2021
1 parent 95c323a commit 4acfc2c
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 31 deletions.
17 changes: 13 additions & 4 deletions .github/workflows/node-iojs.yml
Expand Up @@ -12,6 +12,7 @@ jobs:
- uses: ljharb/actions/node/matrix@main
id: set-matrix
with:
versionsAsRoot: true
preset: 'iojs'

latest:
Expand All @@ -21,7 +22,11 @@ jobs:

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.latest) }}
matrix:
node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
command:
- 'tests-only'
- 'test:corejs'

steps:
- uses: actions/checkout@v2
Expand All @@ -30,7 +35,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
skip-ls-check: true
- run: npm run tests-only
- run: npm run ${{ matrix.command }}
- uses: codecov/codecov-action@v1

minors:
Expand All @@ -42,7 +47,11 @@ jobs:

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.minors) }}
matrix:
node-version: ${{ fromJson(needs.matrix.outputs.minors) }}
command:
- 'tests-only'
- 'test:corejs'

steps:
- uses: actions/checkout@v2
Expand All @@ -51,7 +60,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
skip-ls-check: true
- run: npm run tests-only
- run: npm run ${{ matrix.command }}
- uses: codecov/codecov-action@v1

node:
Expand Down
17 changes: 13 additions & 4 deletions .github/workflows/node-zero.yml
Expand Up @@ -12,6 +12,7 @@ jobs:
- uses: ljharb/actions/node/matrix@main
id: set-matrix
with:
versionsAsRoot: true
preset: '0.x'

stable:
Expand All @@ -21,7 +22,11 @@ jobs:

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.stable) }}
matrix:
node-version: ${{ fromJson(needs.matrix.outputs.stable) }}
command:
- 'tests-only'
- 'test:corejs'

steps:
- uses: actions/checkout@v2
Expand All @@ -31,7 +36,7 @@ jobs:
node-version: ${{ matrix.node-version }}
cache-node-modules-key: node_modules-${{ github.workflow }}-${{ github.action }}-${{ github.run_id }}
skip-ls-check: true
- run: npm run tests-only
- run: npm run ${{ matrix.command }}
- uses: codecov/codecov-action@v1

unstable:
Expand All @@ -43,7 +48,11 @@ jobs:

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.unstable) }}
matrix:
node-version: ${{ fromJson(needs.matrix.outputs.unstable) }}
command:
- 'tests-only'
- 'test:corejs'

steps:
- uses: actions/checkout@v2
Expand All @@ -53,7 +62,7 @@ jobs:
node-version: ${{ matrix.node-version }}
cache-node-modules-key: node_modules-${{ github.workflow }}-${{ github.action }}-${{ github.run_id }}
skip-ls-check: true
- run: npm run tests-only
- run: npm run ${{ matrix.command }}
- uses: codecov/codecov-action@v1

node:
Expand Down
2 changes: 2 additions & 0 deletions .npmignore
Expand Up @@ -11,3 +11,5 @@ coverage
.nyc_output

.github/workflows

./test-core-js.js
25 changes: 20 additions & 5 deletions index.js
Expand Up @@ -19,6 +19,7 @@ var match = String.prototype.match;
var bigIntValueOf = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null;
var gOPS = Object.getOwnPropertySymbols;
var symToString = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol.prototype.toString : null;
var hasShammedSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'object';
var isEnumerable = Object.prototype.propertyIsEnumerable;

var gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPrototypeOf) || (
Expand All @@ -31,7 +32,7 @@ var gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPr

var inspectCustom = require('./util.inspect').custom;
var inspectSymbol = inspectCustom && isSymbol(inspectCustom) ? inspectCustom : null;
var toStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' ? Symbol.toStringTag : null;
var toStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag !== 'undefined' ? Symbol.toStringTag : null;

module.exports = function inspect_(obj, options, depth, seen) {
var opts = options || {};
Expand Down Expand Up @@ -121,8 +122,8 @@ module.exports = function inspect_(obj, options, depth, seen) {
return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + keys.join(', ') + ' }' : '');
}
if (isSymbol(obj)) {
var symString = symToString.call(obj);
return typeof obj === 'object' ? markBoxed(symString) : symString;
var symString = hasShammedSymbols ? String(obj).replace(/^(Symbol\(.*\))_[^)]*$/, '$1') : symToString.call(obj);
return typeof obj === 'object' && !hasShammedSymbols ? markBoxed(symString) : symString;
}
if (isElement(obj)) {
var s = '<' + String(obj.nodeName).toLowerCase();
Expand Down Expand Up @@ -225,6 +226,9 @@ function isBoolean(obj) { return toStr(obj) === '[object Boolean]' && (!toString

// Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives
function isSymbol(obj) {
if (hasShammedSymbols) {
return obj && typeof obj === 'object' && obj instanceof Symbol;
}
if (typeof obj === 'symbol') {
return true;
}
Expand Down Expand Up @@ -432,17 +436,28 @@ function arrObjKeys(obj, inspect) {
xs[i] = has(obj, i) ? inspect(obj[i], obj) : '';
}
}
var syms = typeof gOPS === 'function' ? gOPS(obj) : [];
var symMap;
if (hasShammedSymbols) {
symMap = {};
for (var k = 0; k < syms.length; k++) {
symMap['$' + syms[k]] = syms[k];
}
}

for (var key in obj) { // eslint-disable-line no-restricted-syntax
if (!has(obj, key)) { continue; } // eslint-disable-line no-restricted-syntax, no-continue
if (isArr && String(Number(key)) === key && key < obj.length) { continue; } // eslint-disable-line no-restricted-syntax, no-continue
if ((/[^\w$]/).test(key)) {
if (hasShammedSymbols && symMap['$' + key] instanceof Symbol) {
// this is to prevent shammed Symbols, which are stored as strings, from being included in the string key section
continue; // eslint-disable-line no-restricted-syntax, no-continue
} else if ((/[^\w$]/).test(key)) {
xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj));
} else {
xs.push(key + ': ' + inspect(obj[key], obj));
}
}
if (typeof gOPS === 'function') {
var syms = gOPS(obj);
for (var j = 0; j < syms.length; j++) {
if (isEnumerable.call(obj, syms[j])) {
xs.push('[' + inspect(syms[j]) + ']: ' + inspect(obj[syms[j]], obj));
Expand Down
7 changes: 3 additions & 4 deletions package.json
Expand Up @@ -21,10 +21,9 @@
"prepublishOnly": "safe-publish-latest",
"pretest": "npm run lint",
"lint": "eslint .",
"test": "npm run tests-only",
"tests-only": "nyc npm run tests-only:tape",
"pretests-only:tape": "node test-core-js",
"tests-only:tape": "tape 'test/*.js'",
"test": "npm run tests-only && npm run test:corejs",
"tests-only": "nyc tape 'test/*.js'",
"test:corejs": "nyc tape test-core-js.js 'test/*.js'",
"posttest": "npx aud --production"
},
"testling": {
Expand Down
4 changes: 2 additions & 2 deletions test/bigint.js
Expand Up @@ -2,7 +2,7 @@

var inspect = require('../');
var test = require('tape');
var hasSymbols = require('has-symbols')();
var hasSymbols = require('has-symbols/shams')();

test('bigint', { skip: typeof BigInt === 'undefined' }, function (t) {
t.test('primitives', function (st) {
Expand Down Expand Up @@ -30,7 +30,7 @@ test('bigint', { skip: typeof BigInt === 'undefined' }, function (t) {
st.equal(inspect(Function('return 256n')()), '256n');
});

t.test('toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag !== 'symbol' }, function (st) {
t.test('toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag === 'undefined' }, function (st) {
st.plan(1);

var faker = {};
Expand Down
4 changes: 2 additions & 2 deletions test/fakes.js
Expand Up @@ -2,10 +2,10 @@

var inspect = require('../');
var test = require('tape');
var hasSymbols = require('has-symbols')();
var hasSymbols = require('has-symbols/shams')();
var forEach = require('for-each');

test('fakes', { skip: !hasSymbols || typeof Symbol.toStringTag !== 'symbol' }, function (t) {
test('fakes', { skip: !hasSymbols || typeof Symbol.toStringTag === 'undefined' }, function (t) {
forEach([
'Array',
'Boolean',
Expand Down
20 changes: 17 additions & 3 deletions test/inspect.js
@@ -1,5 +1,5 @@
var test = require('tape');
var hasSymbols = require('has-symbols')();
var hasSymbols = require('has-symbols/shams')();
var utilInspect = require('../util.inspect');
var repeat = require('string.prototype.repeat');

Expand Down Expand Up @@ -43,8 +43,22 @@ test('symbols', { skip: !hasSymbols }, function (t) {
value: 4
});

t.equal(inspect(obj), '{ a: 1, [Symbol(test)]: 2, [Symbol(Symbol.iterator)]: 3 }', 'object with symbols');
t.equal(inspect([obj, []]), '[ { a: 1, [Symbol(test)]: 2, [Symbol(Symbol.iterator)]: 3 }, [] ]', 'object with symbols');
if (typeof Symbol.iterator === 'symbol') {
t.equal(inspect(obj), '{ a: 1, [Symbol(test)]: 2, [Symbol(Symbol.iterator)]: 3 }', 'object with symbols');
t.equal(inspect([obj, []]), '[ { a: 1, [Symbol(test)]: 2, [Symbol(Symbol.iterator)]: 3 }, [] ]', 'object with symbols in array');
} else {
// symbol sham key ordering is unreliable
t.match(
inspect(obj),
/^(?:{ a: 1, \[Symbol\(test\)\]: 2, \[Symbol\(Symbol.iterator\)\]: 3 }|{ a: 1, \[Symbol\(Symbol.iterator\)\]: 3, \[Symbol\(test\)\]: 2 })$/,
'object with symbols (nondeterministic symbol sham key ordering)'
);
t.match(
inspect([obj, []]),
/^\[ (?:{ a: 1, \[Symbol\(test\)\]: 2, \[Symbol\(Symbol.iterator\)\]: 3 }|{ a: 1, \[Symbol\(Symbol.iterator\)\]: 3, \[Symbol\(test\)\]: 2 }), \[\] \]$/,
'object with symbols in array (nondeterministic symbol sham key ordering)'
);
}
});

test('maxStringLength', function (t) {
Expand Down
6 changes: 3 additions & 3 deletions test/toStringTag.js
@@ -1,11 +1,11 @@
'use strict';

var test = require('tape');
var hasSymbols = require('has-symbols')();
var hasSymbols = require('has-symbols/shams')();

var inspect = require('..');
var inspect = require('../');

test('Symbol.toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag !== 'symbol' }, function (t) {
test('Symbol.toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag === 'undefined' }, function (t) {
t.plan(4);

var obj = { a: 1 };
Expand Down
11 changes: 7 additions & 4 deletions test/values.js
Expand Up @@ -2,7 +2,7 @@

var inspect = require('../');
var test = require('tape');
var hasSymbols = require('has-symbols')();
var hasSymbols = require('has-symbols/shams')();

test('values', function (t) {
t.plan(1);
Expand Down Expand Up @@ -68,12 +68,15 @@ test('seen seen seen', function (t) {
);
});

test('symbols', { skip: typeof Symbol !== 'function' }, function (t) {
test('symbols', { skip: !hasSymbols }, function (t) {
var sym = Symbol('foo');
t.equal(inspect(sym), 'Symbol(foo)', 'Symbol("foo") should be "Symbol(foo)"');
t.equal(inspect(Object(sym)), 'Object(Symbol(foo))', 'Object(Symbol("foo")) should be "Object(Symbol(foo))"');
if (typeof sym === 'symbol') {
// Symbol shams are incapable of differentiating boxed from unboxed symbols
t.equal(inspect(Object(sym)), 'Object(Symbol(foo))', 'Object(Symbol("foo")) should be "Object(Symbol(foo))"');
}

t.test('toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag !== 'symbol' }, function (st) {
t.test('toStringTag', { skip: !hasSymbols || typeof Symbol.toStringTag === 'undefined' }, function (st) {
st.plan(1);

var faker = {};
Expand Down

0 comments on commit 4acfc2c

Please sign in to comment.