From 1c9a9f364dc0c37853ae26b975af2d96c5337b89 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 25 Oct 2016 16:55:47 -0400 Subject: [PATCH] Avoid object mutation in `getRawTag`. [closes #2755] --- lodash.js | 18 +++++++++----- test/test.js | 68 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/lodash.js b/lodash.js index c488a491b..e606bcaaf 100644 --- a/lodash.js +++ b/lodash.js @@ -5956,15 +5956,21 @@ * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + try { - var symbol = value[symToStringTag]; value[symToStringTag] = undefined; - } catch (e) { - symbol = undefined; - } + var isSet = true; + } catch (e) {} + var result = nativeObjectToString.call(value); - if (symbol) { - value[symToStringTag] = symbol; + if (isSet) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } } return result; } diff --git a/test/test.js b/test/test.js index f5300aa75..525d7c943 100644 --- a/test/test.js +++ b/test/test.js @@ -11360,7 +11360,13 @@ assert.strictEqual(_.isPlainObject(object), true); }); - QUnit.test('should return `true` for objects with a `Symbol.toStringTag` property', function(assert) { + QUnit.test('should return `true` for objects with a `valueOf` property', function(assert) { + assert.expect(1); + + assert.strictEqual(_.isPlainObject({ 'valueOf': 0 }), true); + }); + + QUnit.test('should return `true` for objects with a writable `Symbol.toStringTag` property', function(assert) { assert.expect(1); if (Symbol && Symbol.toStringTag) { @@ -11374,12 +11380,6 @@ } }); - QUnit.test('should return `true` for objects with a `valueOf` property', function(assert) { - assert.expect(1); - - assert.strictEqual(_.isPlainObject({ 'valueOf': 0 }), true); - }); - QUnit.test('should return `false` for objects with a custom `[[Prototype]]`', function(assert) { assert.expect(1); @@ -11387,25 +11387,6 @@ assert.strictEqual(_.isPlainObject(object), false); }); - QUnit.test('should return `false` for objects with a read-only `Symbol.toStringTag` property', function(assert) { - assert.expect(1); - - if (Symbol && Symbol.toStringTag) { - var object = {}; - defineProperty(object, Symbol.toStringTag, { - 'configurable': true, - 'enumerable': false, - 'writable': false, - 'value': 'X' - }); - - assert.deepEqual(_.isPlainObject(object), false); - } - else { - skipAssert(assert); - } - }); - QUnit.test('should return `false` for DOM elements', function(assert) { assert.expect(1); @@ -11440,6 +11421,41 @@ assert.strictEqual(_.isPlainObject(symbol), false); }); + QUnit.test('should return `false` for objects with a read-only `Symbol.toStringTag` property', function(assert) { + assert.expect(1); + + if (Symbol && Symbol.toStringTag) { + var object = {}; + defineProperty(object, Symbol.toStringTag, { + 'configurable': true, + 'enumerable': false, + 'writable': false, + 'value': 'X' + }); + + assert.deepEqual(_.isPlainObject(object), false); + } + else { + skipAssert(assert); + } + }); + + QUnit.test('should not mutate `value`', function(assert) { + assert.expect(2); + + if (Symbol && Symbol.toStringTag) { + var proto = {}; + proto[Symbol.toStringTag] = undefined; + var object = create(proto); + + assert.strictEqual(_.isPlainObject(object), false); + assert.notOk(lodashStable.has(object, Symbol.toStringTag)); + } + else { + skipAssert(assert, 2); + } + }); + QUnit.test('should work with objects from another realm', function(assert) { assert.expect(1);