Skip to content

Commit

Permalink
Make _.isNative throw if core-js is detected.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalton committed May 15, 2016
1 parent 06e7c96 commit e156459
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 10 deletions.
58 changes: 48 additions & 10 deletions lodash.js
Original file line number Diff line number Diff line change
Expand Up @@ -1233,11 +1233,12 @@
objectProto = context.Object.prototype,
stringProto = context.String.prototype;

/** Used to detect methods masquerading as native. */
var fakeSrcKey = (function() {
var shared = context['__core-js_shared__'],
uid = /[^.]+$/.exec(shared && shared.keys && shared.keys.IE_PROTO || '');
/** Used to detect overreaching core-js shims. */
var coreJsData = context['__core-js_shared__'];

/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
return uid ? ('Symbol(src)_1.' + uid) : '';
}());

Expand Down Expand Up @@ -2994,6 +2995,22 @@
return true;
}

/**
* The base implementation of `_.isNative` without bad shim checks.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a native function,
* else `false`.
*/
function baseIsNative(value) {
if (!isObject(value) || isMasked(value)) {
return false;
}
var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
return pattern.test(toSource(value));
}

/**
* The base implementation of `_.iteratee`.
*
Expand Down Expand Up @@ -5377,7 +5394,7 @@
*/
function getNative(object, key) {
var value = object[key];
return isNative(value) ? value : undefined;
return (isMaskable(value) ? baseIsNative : isNative)(value) ? value : undefined;
}

/**
Expand Down Expand Up @@ -5738,6 +5755,28 @@
return !!data && func === data[0];
}

/**
* Checks if `func` has its source masked.
*
* @private
* @param {Function} func The function to check.
* @returns {boolean} Returns `true` if `func` is masked, else `false`.
*/
function isMasked(func) {
return maskSrcKey in func;
}

/**
* Checks if `func` is capable of being masked.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `func` is maskable, else `false`.
*/
function isMaskable(value) {
return !!coreJsData && isFunction(value);
}

/**
* Checks if `value` is likely a prototype object.
*
Expand Down Expand Up @@ -11138,7 +11177,7 @@
}

/**
* Checks if `value` is a native function.
* Checks if `value` is a pristine native function.
*
* @static
* @memberOf _
Expand All @@ -11156,11 +11195,10 @@
* // => false
*/
function isNative(value) {
if (!isObject(value) || (fakeSrcKey && fakeSrcKey in value)) {
return false;
if (isMaskable(value)) {
throw Error('This method is not supported with core-js. Try https://github.com/es-shims.');
}
var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
return pattern.test(toSource(value));
return baseIsNative(value);
}

/**
Expand Down
42 changes: 42 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11014,6 +11014,48 @@
skipAssert(assert);
}
});

QUnit.test('should throw a if core-js is detected', function(assert) {
assert.expect(1);

if (!isModularize) {
var lodash = _.runInContext({
'__core-js_shared__': {}
});

assert.raises(function() { lodash.isNative(noop); });
}
else {
skipAssert(assert);
}
});

QUnit.test('should detect methods masquerading as native', function(assert) {
assert.expect(2);

if (_._baseEach) {
var path = require('path'),
basePath = path.dirname(filePath),
uid = 'e0gvgyrad1jor',
coreKey = '__core-js_shared__',
fakeSrcKey = 'Symbol(src)_1.' + uid;

root[coreKey] = { 'keys': { 'IE_PROTO': 'Symbol(IE_PROTO)_3.' + uid } };
emptyObject(require.cache);

var baseIsNative = require(path.join(basePath, '_baseIsNative'));
assert.strictEqual(baseIsNative(slice), true);

slice[fakeSrcKey] = slice + '';
assert.strictEqual(baseIsNative(slice), false);

delete slice[fakeSrcKey];
delete root[coreKey];
}
else {
skipAssert(assert, 2);
}
});
}(1, 2, 3));

/*--------------------------------------------------------------------------*/
Expand Down

7 comments on commit e156459

@zloirock
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote - if you will use something like that - I should make it completely undetectable for preventing possible problems. It is better to wait when I'll add marks for completely polyfilled features.

@jdalton
Copy link
Member Author

@jdalton jdalton commented on e156459 May 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @zloirock!

I decided to simply not allow _.isNative to work when core-js is used. No fake key detection and no chance of faulty results that way. The fake detection that is used is for internal use only. I may expand the detection to other libs that mask methods in the future.

In v5, I'll elevate the error from _.isNative to the entire library.
At that time, ~8 months, lodash will simply not run with core-js or other like packages.

@loganfsmyth
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try to have a discussion about this? You both sound pissed and that's no way to have a thought-out discussion. I'm obviously quite out of the loop on this issue, but clearly we need to find some solution here so we can all be happy with.

@jdalton
Copy link
Member Author

@jdalton jdalton commented on e156459 May 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no worries, @loganfsmyth 🙂
With each major release Lodash pushes its modern builds a bit more. This is a progression of that is all.

@loganfsmyth
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally, my reading of

At that time Lodash will simply not run with core-js or other like libs.

is that at that point lodash could never be used alongside Babel however, is that correct, or am I misunderstanding that?

@jdalton
Copy link
Member Author

@jdalton jdalton commented on e156459 May 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that at that point lodash could never be used alongside Babel however, is that correct, or am I misunderstanding that?

Naw. The babel-polyfill package is opt-in. By 2017 I'm sure something will be worked out to allow other shim layers, replace the shim layer, or revise its policies. At the very least we'll have a guide for how to run Babel with Lodash and have shims of some kind.

This patch is more of a bug fix and I suspect the usage of _.isNative is small and even less with this specific scenario. The risk is further reduced by releasing it as a patch bump. If there are widespread issues, of course we'll consider reverting.

This small change with _.isNative won't likely cause too much grief. Further down the line with v5, maybe. But 8 months is a long time and nothing is set in stone for v5 yet. I'm positive an opt-in polyfill package and its overreaching practices can be addressed by then.

I added a doc note to help explain the change in _.isNative behavior.

@jdalton
Copy link
Member Author

@jdalton jdalton commented on e156459 May 24, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated:
Just a heads up: The Lodash v4.13.0+ update, which includes this patch, has been live for over a week now. This release reaches 5,700+ packages as a dependency and 660+ as a dev-dependency. So far, no relevant issues have been reported.

To my knowledge the only issues reported related to this in any way were from Feb 2015 and May 2015. In both cases the root issue was core-js augmenting Object.prototype methods and in both cases Lodash promptly took action to avoid problems. It was only after the May ’15 issue that core-js opt-ed to fake out Lodash.

Please sign in to comment.