Skip to content

Commit

Permalink
[BUGFIX release] Ensure properties on Array.prototype are non-enumera…
Browse files Browse the repository at this point in the history
…ble.

This fixes the following example case:

```js
$.extend(true, {}, {a:['a']})
```
Prior to this change, the above would trigger maximum call stack error.
This is because the `[]` computed property added to the array prototype
references itself, which ultimately makes `$.extend` (and other deep
equality style comparisons) fail.
  • Loading branch information
rwjblue committed Dec 15, 2018
1 parent 3029e9d commit 98afaff
Showing 1 changed file with 17 additions and 5 deletions.
22 changes: 17 additions & 5 deletions packages/@ember/-internals/runtime/lib/mixins/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ export function isArray(_obj) {
return false;
}

/*
This allows us to define computed properties that are not enumerable.
The primary reason this is important is that when `NativeArray` is
applied to `Array.prototype` we need to ensure that we do not add _any_
new enumerable properties.
*/
function nonEnumerableComputed() {
let property = computed(...arguments);
property.enumerable = false;
return property;
}

// ..........................................................
// ARRAY
//
Expand Down Expand Up @@ -273,7 +285,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
@return this
@public
*/
'[]': computed({
'[]': nonEnumerableComputed({
get() {
return this;
},
Expand All @@ -290,7 +302,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
@return {Object | undefined} The first object in the array
@public
*/
firstObject: computed(function() {
firstObject: nonEnumerableComputed(function() {
return objectAt(this, 0);
}).readOnly(),

Expand All @@ -301,7 +313,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
@return {Object | undefined} The last object in the array
@public
*/
lastObject: computed(function() {
lastObject: nonEnumerableComputed(function() {
return objectAt(this, this.length - 1);
}).readOnly(),

Expand Down Expand Up @@ -474,7 +486,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
@property {Boolean} hasArrayObservers
@public
*/
hasArrayObservers: computed(function() {
hasArrayObservers: nonEnumerableComputed(function() {
return hasListeners(this, '@array:change') || hasListeners(this, '@array:before');
}),

Expand Down Expand Up @@ -1154,7 +1166,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
@public
*/
'@each': ARRAY_AT_EACH
? computed(function() {
? nonEnumerableComputed(function() {
deprecate(`Getting the '@each' property on object ${toString(this)} is deprecated`, false, {
id: 'ember-metal.getting-each',
until: '3.5.0',
Expand Down

0 comments on commit 98afaff

Please sign in to comment.