Skip to content

Commit 6dbfbbd

Browse files
teppeistjgq
authored andcommitted
Improve Array.prototype.copyWithin polyfill.
Closes #2902. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=230397978
1 parent 877e304 commit 6dbfbbd

File tree

3 files changed

+86
-17
lines changed

3 files changed

+86
-17
lines changed

src/com/google/javascript/jscomp/js/es6/array/copywithin.js

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
'require util/polyfill';
1818

1919
$jscomp.polyfill('Array.prototype.copyWithin', function(orig) {
20+
// requires strict mode to throw for invalid `this` or params
21+
'use strict';
22+
2023
if (orig) return orig;
2124

2225
/**
@@ -31,32 +34,46 @@ $jscomp.polyfill('Array.prototype.copyWithin', function(orig) {
3134
*/
3235
var polyfill = function(target, start, opt_end) {
3336
var len = this.length;
34-
target = Number(target);
35-
start = Number(start);
36-
opt_end = Number(opt_end != null ? opt_end : len);
37-
if (target < start) {
38-
opt_end = Math.min(opt_end, len);
39-
while (start < opt_end) {
40-
if (start in this) {
41-
this[target++] = this[start++];
37+
target = toInteger(target);
38+
start = toInteger(start);
39+
var end = opt_end === undefined ? len : toInteger(opt_end);
40+
var to = target < 0 ? Math.max(len + target, 0) : Math.min(target, len);
41+
var from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
42+
var final = end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
43+
if (to < from) {
44+
while (from < final) {
45+
if (from in this) {
46+
this[to++] = this[from++];
4247
} else {
43-
delete this[target++];
44-
start++;
48+
delete this[to++];
49+
from++;
4550
}
4651
}
4752
} else {
48-
opt_end = Math.min(opt_end, len + start - target);
49-
target += opt_end - start;
50-
while (opt_end > start) {
51-
if (--opt_end in this) {
52-
this[--target] = this[opt_end];
53+
final = Math.min(final, len + from - to);
54+
to += final - from;
55+
while (final > from) {
56+
if (--final in this) {
57+
this[--to] = this[final];
5358
} else {
54-
delete this[target];
59+
delete this[--to];
5560
}
5661
}
5762
}
5863
return this;
5964
};
6065

66+
/**
67+
* @param {number} arg
68+
* @return {number}
69+
*/
70+
function toInteger(arg) {
71+
var n = Number(arg);
72+
if (n === Infinity || n === -Infinity) {
73+
return n;
74+
}
75+
return n | 0;
76+
}
77+
6178
return polyfill;
6279
}, 'es6', 'es3');

src/com/google/javascript/jscomp/resources.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

test/com/google/javascript/jscomp/runtime_tests/polyfill_tests/array_copywithin_test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,57 @@ testSuite({
6464
arr = {length: 3, 1: 4, 3: 'unused'};
6565
assertEquals(arr, Array.prototype.copyWithin.call(noCheck(arr), 0, 1));
6666
assertObjectEquals({length: 3, 0: 4, 3: 'unused'}, arr);
67+
68+
arr = {length: 3, 1: 4, 3: 'unused'};
69+
assertEquals(arr, Array.prototype.copyWithin.call(noCheck(arr), 1, 0));
70+
assertObjectEquals({length: 3, 2: 4, 3: 'unused'}, arr);
71+
},
72+
73+
testCopyWithin_coercingArgs() {
74+
assertObjectEquals([1, 2, 3, 3], [0, 1, 2, 3].copyWithin(NaN, 1));
75+
assertObjectEquals([1, 2, 3, 3], [0, 1, 2, 3].copyWithin(0.5, 1));
76+
assertObjectEquals([0, 0, 1, 2], [0, 1, 2, 3].copyWithin(1.5, 0));
77+
78+
assertObjectEquals([0, 0, 1, 2], [0, 1, 2, 3].copyWithin(1, NaN));
79+
assertObjectEquals([0, 0, 1, 2], [0, 1, 2, 3].copyWithin(1, 0.5));
80+
assertObjectEquals([1, 2, 3, 3], [0, 1, 2, 3].copyWithin(0, 1.5));
81+
82+
assertObjectEquals([0, 1, 2, 3], [0, 1, 2, 3].copyWithin(1, 0, NaN));
83+
assertObjectEquals([0, 0, 2, 3], [0, 1, 2, 3].copyWithin(1, 0, 1.5));
84+
assertObjectEquals([0, 0, 1, 3], [0, 1, 2, 3].copyWithin(1, 0, -2.5));
85+
assertObjectEquals([0, 0, 1, 2], [0, 1, 2, 3].copyWithin(1, 0, undefined));
86+
},
87+
88+
testCopyWithin_negativeArgs() {
89+
assertObjectEquals([1, 2, 3, 1, 2], [1, 2, 3, 4, 5].copyWithin(-2, 0));
90+
assertObjectEquals([1, 3, 4, 5, 5], [1, 2, 3, 4, 5].copyWithin(1, -3));
91+
assertObjectEquals([1, 3, 4, 4, 5], [1, 2, 3, 4, 5].copyWithin(1, 2, -1));
92+
},
93+
94+
testCopyWithin_throwsIfNullish() {
95+
// TODO(tjgq): requires strict mode, lost in transpilation (b/24413211)
96+
// assertThrows(function() {
97+
// Array.prototype.copyWithin.call(null, 0, 0);
98+
// });
99+
// assertThrows(function() {
100+
// Array.prototype.copyWithin.call(undefined, 0, 0);
101+
// });
102+
},
103+
104+
testCopyWithin_throwIfFailToDeleteProperty() {
105+
var obj = {
106+
length: 5
107+
};
108+
109+
Object.defineProperty(obj, '4', {
110+
value: 'a',
111+
configurable: false,
112+
writable: true
113+
});
114+
115+
// TODO(tjgq): requires strict mode, lost in transpilation (b/24413211)
116+
// assertThrows(function() {
117+
// Array.prototype.copyWithin.call(noCheck(obj), 4, 0);
118+
// });
67119
},
68120
});

0 commit comments

Comments
 (0)