Attachment #8453010: copywithin.patch for bug #1021379

View | Details | Raw Unified | Return to bug 1021379
Collapse All | Expand All

(-)a/js/src/jit-test/tests/basic/typed-array-copywithin.js (+170 lines)
Line     Link Here 
Line 0    Link Here 
1
// Bug 1021379 - Rename typed arrays' move method to copyWithin,
2
// fix up to ES6 semantics
3
// Tests for TypedArray#copyWithin
4
5
load(libdir + "asserts.js");
6
7
const constructors = [
8
  Int8Array,
9
  Uint8Array,
10
  Uint8ClampedArray,
11
  Int16Array,
12
  Uint16Array,
13
  Int32Array,
14
  Uint32Array,
15
  Float32Array,
16
  Float64Array
17
];
18
19
for (constructor of constructors) {
20
21
    assertEq(constructor.prototype.copyWithin.length, 2);
22
23
    // works with two arguments
24
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, 3),
25
		  new constructor([4, 5, 3, 4, 5]));
26
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(1, 3),
27
		  new constructor([1, 4, 5, 4, 5]));
28
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(1, 2),
29
		  new constructor([1, 3, 4, 5, 5]));
30
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(2, 2),
31
		  new constructor([1, 2, 3, 4, 5]));
32
33
    // works with three arguments
34
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, 3, 4),
35
		  new constructor([4, 2, 3, 4, 5]));
36
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(1, 3, 4),
37
		  new constructor([1, 4, 3, 4, 5]));
38
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(1, 2, 4),
39
		  new constructor([1, 3, 4, 4, 5]));
40
41
    // works with negative arguments
42
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, -2),
43
		  new constructor([4, 5, 3, 4, 5]));
44
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, -2, -1),
45
		  new constructor([4, 2, 3, 4, 5]));
46
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-4, -3, -2),
47
		  new constructor([1, 3, 3, 4, 5]));
48
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-4, -3, -1),
49
		  new constructor([1, 3, 4, 4, 5]));
50
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-4, -3),
51
		  new constructor([1, 3, 4, 5, 5]));
52
53
    // throws on null/undefined values
54
    assertThrowsInstanceOf(function() {
55
	constructor.prototype.copyWithin.call(null, 0, 3);
56
    }, TypeError, "Assert that copyWithin fails if this value is null");
57
58
    assertThrowsInstanceOf(function() {
59
	constructor.prototype.copyWithin.call(undefined, 0, 3);
60
    }, TypeError, "Assert that copyWithin fails if this value is undefined");
61
62
    // test with this value as string
63
    assertThrowsInstanceOf(function() {
64
	constructor.prototype.copyWithin.call("hello world", 0, 3);
65
    }, TypeError, "Assert that copyWithin fails if this value is string");
66
67
    // if arguments object is sloppy, copyWithin must move the arguments around
68
    function f(a, b, c, d, e) {
69
	[].copyWithin.call(arguments, 1, 3);
70
	return new constructor([a, b, c, d, e]);
71
    }
72
    assertDeepEq(f(1, 2, 3, 4, 5), (new constructor([1, 4, 5, 4, 5])));
73
74
    // test with target > start on 2 arguments
75
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(3, 0),
76
		 (new constructor([1, 2, 3, 1, 2])));
77
78
    // test with target > start on 3 arguments
79
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(3, 0, 4),
80
		 (new constructor([1, 2, 3, 1, 2])));
81
82
    // test on array with holes
83
    var arr = new constructor(6);
84
    for (var i = 0; i < arr.length; i += 2) {
85
	arr[i] = i;
86
    }
87
    assertDeepEq(arr.copyWithin(0, 3), (new constructor([, 4, , , 4, , ])));
88
89
    // test on fractional arguments
90
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0.2, 3.9),
91
		 (new constructor([4, 5, 3, 4, 5])));
92
93
    // test with -0
94
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-0, 3),
95
		 (new constructor([4, 5, 3, 4, 5])));
96
97
    // test with arguments more than this.length
98
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, 7),
99
		 (new constructor([1, 2, 3, 4, 5])));
100
101
    // test with arguments less than -this.length
102
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-7, 0),
103
		 (new constructor([1, 2, 3, 4, 5])));
104
105
    // test with arguments equal to -this.length
106
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(-5, 0),
107
		 (new constructor([1, 2, 3, 4, 5])));
108
109
    // test on empty constructor
110
    assertDeepEq((new constructor([])).copyWithin(0, 3), (new constructor([])));
111
112
    // test with target range being shorter than end - start
113
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(2, 1, 4),
114
		 (new constructor([1, 2, 2, 3, 4])));
115
116
    // test overlapping ranges
117
    arr = new constructor([1, 2, 3, 4, 5]);
118
    arr.copyWithin(2, 1, 4);
119
    assertDeepEq(arr.copyWithin(2, 1, 4), (new constructor([1, 2, 2, 2, 3])));
120
121
    // undefined as third argument
122
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, 3, undefined),
123
		 (new constructor([4, 5, 3, 4, 5])));
124
125
    // test that this.length is never called
126
    arr = new constructor([0, 1, 2, 3, 5]);
127
    Object.defineProperty(arr, "length", {
128
	get: function () { throw new Error("length accessor called"); }
129
    });
130
    arr.copyWithin(1, 3);
131
132
    var large = 10000;
133
134
    // test on a large constructor
135
    arr = new constructor(large);
136
    assertDeepEq(arr.copyWithin(45, 900), arr);
137
138
    // test on floating point numbers
139
    for (var i = 0; i < large; i++) {
140
	arr[i] = Math.random();
141
    }
142
    arr.copyWithin(45, 900);
143
144
    // test on constructor of objects
145
    for (var i = 0; i < large; i++) {
146
	arr[i] = { num: Math.random() };
147
    }
148
    arr.copyWithin(45, 900);
149
150
    // test constructor length remains same
151
    assertEq(arr.length, large);
152
153
    // test null on third argument is handled correctly
154
    assertDeepEq((new constructor([1, 2, 3, 4, 5])).copyWithin(0, 3, null),
155
        (new constructor([1, 2, 3, 4, 5])));
156
157
    // test with a proxy object
158
    var proxyObj = {
159
	get: function(recipient, name) {
160
	    return recipient[name] + 2;
161
	}
162
    };
163
164
    var p = new Proxy(new constructor([1, 2, 3, 4, 5]), proxyObj);
165
166
    assertThrowsInstanceOf(function() {
167
	constructor.prototype.copyWithin.call(p, 0, 3);
168
    }, TypeError, "Assert that copyWithin fails if used with a proxy object");
169
170
}
(-)a/js/src/vm/TypedArrayObject.cpp (-41 / +45 lines)
Line     Link Here 
 Lines 522-604   class TypedArrayObjectTemplate : public Link Here 
522
    static bool
522
    static bool
523
    fun_subarray(JSContext *cx, unsigned argc, Value *vp)
523
    fun_subarray(JSContext *cx, unsigned argc, Value *vp)
524
    {
524
    {
525
        CallArgs args = CallArgsFromVp(argc, vp);
525
        CallArgs args = CallArgsFromVp(argc, vp);
526
        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
526
        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
527
                                    ThisTypedArrayObject::fun_subarray_impl>(cx, args);
527
                                    ThisTypedArrayObject::fun_subarray_impl>(cx, args);
528
    }
528
    }
529
529
530
    /* move(begin, end, dest) */
530
    /* copyWithin(target, start[, end]) */
531
    // ES6 draft rev 25, 22.2.3.5
531
    static bool
532
    static bool
532
    fun_move_impl(JSContext *cx, CallArgs args)
533
    fun_copyWithin_impl(JSContext *cx, CallArgs args)
533
    {
534
    {
534
        JS_ASSERT(IsThisClass(args.thisv()));
535
        JS_ASSERT(IsThisClass(args.thisv()));
535
        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
536
        if (args.length() < 2) {
536
537
                  JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
537
        if (args.length() < 3) {
538
                  return false;
538
            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
539
            return false;
540
        }
539
        }
541
540
        // Steps 1-2.
542
        uint32_t srcBegin;
541
        Rooted<TypedArrayObject*> O(cx, &args.thisv().toObject().as<TypedArrayObject>());
543
        uint32_t srcEnd;
542
544
        uint32_t dest;
543
        uint32_t from;
545
544
        uint32_t final;
546
        uint32_t originalLength = tarray->length();
545
        uint32_t to;
547
        if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) ||
546
548
            !ToClampedIndex(cx, args[1], originalLength, &srcEnd) ||
547
        // Steps 3-4.
549
            !ToClampedIndex(cx, args[2], originalLength, &dest))
548
        uint32_t len = O->length();
549
550
        // Steps 6-8.
551
        if (!ToClampedIndex(cx, args[0], len, &to) ||
552
        // Steps 9-11.
553
            !ToClampedIndex(cx, args[1], len, &from))
550
        {
554
        {
551
            return false;
555
            return false;
552
        }
556
        }
553
557
554
        if (srcBegin > srcEnd) {
558
        // Step 12.
555
            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
559
        if (args.get(2).isUndefined())
556
            return false;
560
          final = len;
557
        }
561
        // Steps 13-14.
558
562
        else if (!ToClampedIndex(cx, args[2], len, &final))
559
        uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex
563
          return false;
560
        uint32_t nelts = srcEnd - srcBegin;
564
561
565
        // Steps 15-18.
562
        MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31");
566
        uint32_t lengthDuringMove = O->length(); // beware ToClampedIndex
563
        MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31");
567
        uint32_t count = (final < from || lengthDuringMove < to) ? 0 : Min(final - from, lengthDuringMove - to);
564
        if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) {
568
565
            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
569
        MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
566
            return false;
570
        MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
567
        }
571
568
572
        uint32_t byteDest = to * sizeof(NativeType);
569
        uint32_t byteDest = dest * sizeof(NativeType);
573
        uint32_t byteSrc = from * sizeof(NativeType);
570
        uint32_t byteSrc = srcBegin * sizeof(NativeType);
574
        uint32_t byteSize = count * sizeof(NativeType);
571
        uint32_t byteSize = nelts * sizeof(NativeType);
572
575
573
#ifdef DEBUG
576
#ifdef DEBUG
574
        uint32_t viewByteLength = tarray->byteLength();
577
        uint32_t viewByteLength = O->byteLength();
575
        JS_ASSERT(byteDest <= viewByteLength);
578
        JS_ASSERT(byteDest <= viewByteLength);
576
        JS_ASSERT(byteSrc <= viewByteLength);
579
        JS_ASSERT(byteSrc <= viewByteLength);
577
        JS_ASSERT(byteDest + byteSize <= viewByteLength);
580
        JS_ASSERT(byteDest + byteSize <= viewByteLength);
578
        JS_ASSERT(byteSrc + byteSize <= viewByteLength);
581
        JS_ASSERT(byteSrc + byteSize <= viewByteLength);
579
582
580
        // Should not overflow because size is limited to 2^31
583
        // Should not overflow because size is limited to 2^31
581
        JS_ASSERT(byteDest + byteSize >= byteDest);
584
        JS_ASSERT(byteDest + byteSize >= byteDest);
582
        JS_ASSERT(byteSrc + byteSize >= byteSrc);
585
        JS_ASSERT(byteSrc + byteSize >= byteSrc);
583
#endif
586
#endif
584
587
585
        uint8_t *data = static_cast<uint8_t*>(tarray->viewData());
588
        uint8_t *data = static_cast<uint8_t*>(O->viewData());
586
        memmove(&data[byteDest], &data[byteSrc], byteSize);
589
        memcpy(&data[byteDest], &data[byteSrc], byteSize);
587
        args.rval().setUndefined();
590
        // Step 19.
591
        args.rval().set(args.thisv());
588
        return true;
592
        return true;
589
    }
593
    }
590
594
591
    static bool
595
    static bool
592
    fun_move(JSContext *cx, unsigned argc, Value *vp)
596
    fun_copyWithin(JSContext *cx, unsigned argc, Value *vp)
593
    {
597
    {
594
        CallArgs args = CallArgsFromVp(argc, vp);
598
        CallArgs args = CallArgsFromVp(argc, vp);
595
        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
599
        return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
596
                                    ThisTypedArrayObject::fun_move_impl>(cx, args);
600
                                    ThisTypedArrayObject::fun_copyWithin_impl>(cx, args);
597
    }
601
    }
598
602
599
    /* set(array[, offset]) */
603
    /* set(array[, offset]) */
600
    static bool
604
    static bool
601
    fun_set_impl(JSContext *cx, CallArgs args)
605
    fun_set_impl(JSContext *cx, CallArgs args)
602
    {
606
    {
603
        JS_ASSERT(IsThisClass(args.thisv()));
607
        JS_ASSERT(IsThisClass(args.thisv()));
604
        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
608
        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
 Lines 2033-2049   TypedArrayObject::setElement(TypedArrayO Link Here 
2033
 *** JS impl
2037
 *** JS impl
2034
 ***/
2038
 ***/
2035
2039
2036
/*
2040
/*
2037
 * TypedArrayObject boilerplate
2041
 * TypedArrayObject boilerplate
2038
 */
2042
 */
2039
2043
2040
#ifndef RELEASE_BUILD
2044
#ifndef RELEASE_BUILD
2041
# define EXPERIMENTAL_FUNCTIONS(_t) JS_FN("move", _t##Object::fun_move, 3, JSFUN_GENERIC_NATIVE),
2045
# define EXPERIMENTAL_FUNCTIONS(_t) JS_FN("copyWithin", _t##Object::fun_copyWithin, 2, JSFUN_GENERIC_NATIVE),
2042
#else
2046
#else
2043
# define EXPERIMENTAL_FUNCTIONS(_t)
2047
# define EXPERIMENTAL_FUNCTIONS(_t)
2044
#endif
2048
#endif
2045
2049
2046
#define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                      \
2050
#define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                      \
2047
const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
2051
const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
2048
    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
2052
    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
2049
    JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
2053
    JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \

Return to bug 1021379