Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix(*): do not rely on an object's hasOwnProperty #2141

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

////////////////////////////////////

/**
* hasOwnProperty may be overriden by a property of the same name, or entirely
* absent from an object that does not inherit Object.prototype; this copy is
* used instead
*/
var hasOwn = Object.prototype.hasOwnProperty;

/**
* @ngdoc function
* @name angular.lowercase
Expand Down Expand Up @@ -139,7 +146,7 @@ function forEach(obj, iterator, context) {
if (obj) {
if (isFunction(obj)){
for (key in obj) {
if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
if (key != 'prototype' && key != 'length' && key != 'name' && hasOwn.call(obj, key)) {
iterator.call(context, obj[key], key);
}
}
Expand All @@ -150,7 +157,7 @@ function forEach(obj, iterator, context) {
iterator.call(context, obj[key], key);
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (hasOwn.call(obj, key)) {
iterator.call(context, obj[key], key);
}
}
Expand All @@ -162,7 +169,7 @@ function forEach(obj, iterator, context) {
function sortedKeys(obj) {
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (hasOwn.call(obj, key)) {
keys.push(key);
}
}
Expand Down Expand Up @@ -508,7 +515,7 @@ function size(obj, ownPropsOnly) {
return obj.length;
} else if (isObject(obj)){
for (key in obj)
if (!ownPropsOnly || obj.hasOwnProperty(key))
if (!ownPropsOnly || hasOwn.call(obj, key))
size++;
}

Expand Down Expand Up @@ -609,7 +616,7 @@ function shallowCopy(src, dst) {
dst = dst || {};

for(var key in src) {
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
if (hasOwn.call(src, key) && key.substr(0, 2) !== '$$') {
dst[key] = src[key];
}
}
Expand Down Expand Up @@ -667,7 +674,7 @@ function equals(o1, o2) {
keySet[key] = true;
}
for(key in o2) {
if (!keySet[key] &&
if (!hasOwn.call(keySet, key) &&
key.charAt(0) !== '$' &&
o2[key] !== undefined &&
!isFunction(o2[key])) return false;
Expand Down
2 changes: 1 addition & 1 deletion src/angular-bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@

if (IGNORE[prop] || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing
continue;
} else if (!globalVars.hasOwnProperty(varKey)) {
} else if (!hasOwn.call(globalVars, varKey)) {
//console.log('new global variable found: ', prop);
try {
globalVars[varKey] = window[prop];
Expand Down
4 changes: 2 additions & 2 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ function createInjector(modulesToLoad) {
if (typeof serviceName !== 'string') {
throw Error('Service name expected');
}
if (cache.hasOwnProperty(serviceName)) {
if (hasOwn.call(cache, serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw Error('Circular dependency: ' + path.join(' <- '));
}
Expand All @@ -554,7 +554,7 @@ function createInjector(modulesToLoad) {
for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
args.push(
locals && locals.hasOwnProperty(key)
locals && hasOwn.call(locals, key)
? locals[key]
: getService(key)
);
Expand Down
2 changes: 1 addition & 1 deletion src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function setupModuleLoader(window) {
* @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
if (requires && modules.hasOwnProperty(name)) {
if (requires && hasOwn.call(modules, name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
Expand Down
6 changes: 3 additions & 3 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ function $CompileProvider($provide) {
this.directive = function registerDirective(name, directiveFactory) {
if (isString(name)) {
assertArg(directiveFactory, 'directive');
if (!hasDirectives.hasOwnProperty(name)) {
if (!hasOwn.call(hasDirectives, name)) {
hasDirectives[name] = [];
$provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
function($injector, $exceptionHandler) {
Expand Down Expand Up @@ -917,7 +917,7 @@ function $CompileProvider($provide) {
*/
function addDirective(tDirectives, name, location, maxPriority) {
var match = false;
if (hasDirectives.hasOwnProperty(name)) {
if (hasOwn.call(hasDirectives, name)) {
for(var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i<ii; i++) {
try {
Expand Down Expand Up @@ -964,7 +964,7 @@ function $CompileProvider($provide) {
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
} else if (key == 'style') {
$element.attr('style', $element.attr('style') + ';' + value);
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
} else if (key.charAt(0) != '$' && !hasOwn.call(dst, key)) {
dst[key] = value;
dstAttr[key] = srcAttr[key];
}
Expand Down
2 changes: 1 addition & 1 deletion src/ng/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function $ControllerProvider() {
return function(constructor, locals) {
if(isString(constructor)) {
var name = constructor;
constructor = controllers.hasOwnProperty(name)
constructor = hasOwn.call(controllers, name)
? controllers[name]
: getter(locals.$scope, name, true) || getter($window, name, true);

Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function FormController(element, attrs) {
form.$addControl = function(control) {
controls.push(control);

if (control.$name && !form.hasOwnProperty(control.$name)) {
if (control.$name && !hasOwn.call(form, control.$name)) {
form[control.$name] = control;
}
};
Expand Down
4 changes: 2 additions & 2 deletions src/ng/directive/ngRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ var ngRepeatDirective = ngDirective({
// if object, extract keys, sort them and use to determine order of iteration over obj props
array = [];
for(key in collection) {
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
if (hasOwn.call(collection, key) && key.charAt(0) != '$') {
array.push(key);
}
}
Expand Down Expand Up @@ -172,7 +172,7 @@ var ngRepeatDirective = ngDirective({

//shrink children
for (key in lastOrder) {
if (lastOrder.hasOwnProperty(key)) {
if (hasOwn.call(lastOrder, key)) {
array = lastOrder[key];
while(array.length) {
value = array.pop();
Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {


self.hasOption = function(value) {
return optionsMap.hasOwnProperty(value);
return hasOwn.call(optionsMap, value);
}

$scope.$on('$destroy', function() {
Expand Down
10 changes: 5 additions & 5 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ function lex(text, csp){
text:ident
};

if (OPERATORS.hasOwnProperty(ident)) {
if (hasOwn.call(OPERATORS, ident)) {
token.fn = token.json = OPERATORS[ident];
} else {
var getter = getterFn(ident, csp);
Expand Down Expand Up @@ -732,7 +732,7 @@ var getterFnCache = {};
*/
function cspSafeGetterFn(key0, key1, key2, key3, key4) {
return function(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
var pathVal = (locals && hasOwn.call(locals, key0)) ? locals : scope,
promise;

if (pathVal === null || pathVal === undefined) return pathVal;
Expand Down Expand Up @@ -795,7 +795,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
};

function getterFn(path, csp) {
if (getterFnCache.hasOwnProperty(path)) {
if (hasOwn.call(getterFnCache, path)) {
return getterFnCache[path];
}

Expand Down Expand Up @@ -827,7 +827,7 @@ function getterFn(path, csp) {
// we simply dereference 's' on any .dot notation
? 's'
// but if we are first then we check locals first, and if so read it first
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
: '((k&&hasOwn.call(k,"' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
'if (s && s.then) {\n' +
' if (!("$$v" in s)) {\n' +
' p=s;\n' +
Expand Down Expand Up @@ -892,7 +892,7 @@ function $ParseProvider() {
return function(exp) {
switch(typeof exp) {
case 'string':
return cache.hasOwnProperty(exp)
return hasOwn.call(cache, exp)
? cache[exp]
: cache[exp] = parser(exp, false, $filter, $sniffer.csp);
case 'function':
Expand Down
4 changes: 2 additions & 2 deletions src/ng/q.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,11 +391,11 @@ function qFactory(nextTick, exceptionHandler) {
forEach(promises, function(promise, key) {
counter++;
ref(promise).then(function(value) {
if (results.hasOwnProperty(key)) return;
if (hasOwn.call(results, key)) return;
results[key] = value;
if (!(--counter)) deferred.resolve(results);
}, function(reason) {
if (results.hasOwnProperty(key)) return;
if (hasOwn.call(results, key)) return;
deferred.reject(reason);
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/ngResource/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ angular.module('ngResource', ['ng']).

params = params || {};
forEach(self.urlParams, function(_, urlParam){
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
val = Object.prototype.hasOwnProperty.call(params, urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1");
Expand Down
22 changes: 22 additions & 0 deletions test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ describe('angular', function() {
expect(equals(new Date(0), 0)).toBe(false);
expect(equals(0, new Date(0))).toBe(false);
});

it('should correctly test for keys that are present on Object.prototype', function() {
expect(equals({}, {hasOwnProperty: 1})).toBe(false);
expect(equals({}, {toString: null})).toBe(false);
});
});

describe('size', function() {
Expand All @@ -231,6 +236,10 @@ describe('angular', function() {
expect(size(obj, true)).toBe(3);
});

it('when counting own properties should work well with objects that define "hasOwnProperty"', function() {
expect(size({hasOwnProperty: 1, foo: 2}, true)).toBe(2);
});

it('should return the string length', function() {
expect(size('')).toBe(0);
expect(size('abc')).toBe(3);
Expand Down Expand Up @@ -357,6 +366,19 @@ describe('angular', function() {
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['length:2', 'foo:bar']);
});

it('should handle objects that define a "hasOwnProperty" key', function() {
var obj = {
foo: 1,
hasOwnProperty: 2
},
log = [];

forEach(obj, function(value, key) {
log.push(key + ':' + value);
});
expect(log).toEqual(['foo:1', 'hasOwnProperty:2']);
});
});


Expand Down