Skip to content

Commit e45d613

Browse files
committed
Fix cycle detection
See emberjs/ember.js#17220
1 parent acc7e6e commit e45d613

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

addon/copy.js

+18-7
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,44 @@ function _copy(obj, deep, seen, copies) {
1515
return copies[loc];
1616
}
1717

18+
if (deep) {
19+
seen.push(obj);
20+
}
21+
1822
// IMPORTANT: this specific test will detect a native array only. Any other
1923
// object will need to implement Copyable.
2024
if (Array.isArray(obj)) {
2125
ret = obj.slice();
2226

2327
if (deep) {
28+
copies.push(ret);
2429
loc = ret.length;
2530

2631
while (--loc >= 0) {
2732
ret[loc] = _copy(ret[loc], deep, seen, copies);
2833
}
2934
}
3035
} else if (Copyable.detect(obj)) {
31-
ret = obj.copy(deep, seen, copies);
36+
ret = obj.copy(deep, seen, copies);
37+
if (deep) {
38+
copies.push(ret);
39+
}
3240
} else if (obj instanceof Date) {
3341
ret = new Date(obj.getTime());
42+
if (deep) {
43+
copies.push(ret);
44+
}
3445
} else {
3546
assert(
3647
'Cannot clone an EmberObject that does not implement Copyable',
37-
!(obj instanceof EmberObject)
48+
!(obj instanceof EmberObject) || Copyable.detect(obj)
3849
);
39-
50+
4051
ret = {};
52+
if (deep) {
53+
copies.push(ret);
54+
}
55+
4156
let key;
4257
for (key in obj) {
4358
// support Null prototype
@@ -54,10 +69,6 @@ function _copy(obj, deep, seen, copies) {
5469
ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key];
5570
}
5671
}
57-
if (deep) {
58-
seen.push(obj);
59-
copies.push(ret);
60-
}
6172

6273
return ret;
6374
}

tests/unit/copy-test.js

+14
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,18 @@ module('Unit | Utility | copy', function(/*hooks*/) {
2828
assert.deepEqual(array, arrayCopy, 'array content cloned successfully in new array');
2929
});
3030

31+
32+
test('copy cycle detection', function(assert) {
33+
let obj = {
34+
foo: {
35+
bar: 'bar',
36+
},
37+
};
38+
obj.foo.foo = obj.foo;
39+
let cycleCopy = copy(obj, true);
40+
41+
assert.equal(cycleCopy.foo.bar, 'bar');
42+
assert.notEqual(cycleCopy.foo.foo, obj.foo.foo);
43+
assert.strictEqual(cycleCopy.foo.foo, cycleCopy.foo.foo);
44+
});
3145
});

0 commit comments

Comments
 (0)