Skip to content

Commit 5186377

Browse files
Robert Jacksonkategengler
authored andcommitted
[BUGFIX release] Ensure properties on Array.prototype are non-enumerable.
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. (cherry picked from commit 98afaff)
1 parent b64aa3a commit 5186377

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

  • packages/@ember/-internals/runtime/lib/mixins

packages/@ember/-internals/runtime/lib/mixins/array.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ export function isArray(_obj) {
171171
return false;
172172
}
173173

174+
/*
175+
This allows us to define computed properties that are not enumerable.
176+
The primary reason this is important is that when `NativeArray` is
177+
applied to `Array.prototype` we need to ensure that we do not add _any_
178+
new enumerable properties.
179+
*/
180+
function nonEnumerableComputed() {
181+
let property = computed(...arguments);
182+
property.enumerable = false;
183+
return property;
184+
}
185+
174186
// ..........................................................
175187
// ARRAY
176188
//
@@ -273,7 +285,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
273285
@return this
274286
@public
275287
*/
276-
'[]': computed({
288+
'[]': nonEnumerableComputed({
277289
get() {
278290
return this;
279291
},
@@ -290,7 +302,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
290302
@return {Object | undefined} The first object in the array
291303
@public
292304
*/
293-
firstObject: computed(function() {
305+
firstObject: nonEnumerableComputed(function() {
294306
return objectAt(this, 0);
295307
}).readOnly(),
296308

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

@@ -474,7 +486,7 @@ const ArrayMixin = Mixin.create(Enumerable, {
474486
@property {Boolean} hasArrayObservers
475487
@public
476488
*/
477-
hasArrayObservers: computed(function() {
489+
hasArrayObservers: nonEnumerableComputed(function() {
478490
return hasListeners(this, '@array:change') || hasListeners(this, '@array:before');
479491
}),
480492

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

0 commit comments

Comments
 (0)