Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(copy): handle multiple references to ArrayBuffers #13640

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,10 @@ function isTypedArray(value) {
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
}

function isArrayBuffer(obj) {
return toString.call(obj) === '[object ArrayBuffer]';
}


var trim = function(value) {
return isString(value) ? value.trim() : value;
Expand Down Expand Up @@ -807,7 +811,7 @@ function copy(source, destination) {
var stackDest = [];

if (destination) {
if (isTypedArray(destination)) {
if (isTypedArray(destination) || isArrayBuffer(destination)) {
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
}
if (source === destination) {
Expand Down Expand Up @@ -907,7 +911,16 @@ function copy(source, destination) {
case '[object Uint8ClampedArray]':
case '[object Uint16Array]':
case '[object Uint32Array]':
return new source.constructor(source);
return new source.constructor(copyElement(source.buffer));

case '[object ArrayBuffer]':
//Support: IE10
if (!source.slice) {
var copied = new ArrayBuffer(source.byteLength);
new Uint8Array(copied).set(new Uint8Array(source));
return copied;
}
return source.slice(0);

case '[object Boolean]':
case '[object Number]':
Expand Down
60 changes: 60 additions & 0 deletions test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('angular', function() {
expect(copy(src) instanceof Uint8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -97,6 +98,7 @@ describe('angular', function() {
expect(copy(src) instanceof Uint8ClampedArray).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -108,6 +110,7 @@ describe('angular', function() {
expect(copy(src) instanceof Uint16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -119,6 +122,7 @@ describe('angular', function() {
expect(copy(src) instanceof Uint32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -130,6 +134,7 @@ describe('angular', function() {
expect(copy(src) instanceof Int8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -141,6 +146,7 @@ describe('angular', function() {
expect(copy(src) instanceof Int16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -152,6 +158,7 @@ describe('angular', function() {
expect(copy(src) instanceof Int32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -163,6 +170,7 @@ describe('angular', function() {
expect(copy(src) instanceof Float32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

Expand All @@ -174,6 +182,49 @@ describe('angular', function() {
expect(copy(src) instanceof Float64Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});

it('should copy an ArrayBuffer with no destination', function() {
if (typeof ArrayBuffer !== 'undefined') {
var src = new ArrayBuffer(8);
new Int32Array(src).set([1, 2]);

var dst = copy(src);
expect(dst instanceof ArrayBuffer).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});

it('should handle ArrayBuffer objects with multiple references', function() {
if (typeof ArrayBuffer !== 'undefined') {
var buffer = new ArrayBuffer(8);
var src = [new Int32Array(buffer), new Float32Array(buffer)];
src[0].set([1, 2]);

var dst = copy(src);
expect(dst).toEqual(src);
expect(dst[0]).not.toBe(src[0]);
expect(dst[1]).not.toBe(src[1]);
expect(dst[0].buffer).toBe(dst[1].buffer);
expect(dst[0].buffer).not.toBe(buffer);
}
});

it('should handle Int32Array objects with multiple references', function() {
if (typeof Int32Array !== 'undefined') {
var arr = new Int32Array(2);
var src = [arr, arr];
arr.set([1, 2]);

var dst = copy(src);
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst[0]).not.toBe(src[0]);
expect(dst[0]).toBe(dst[1]);
expect(dst[0].buffer).toBe(dst[1].buffer);
}
});

Expand Down Expand Up @@ -258,6 +309,15 @@ describe('angular', function() {
}
});

it("should throw an exception if an ArrayBuffer is the destination", function() {
if (typeof ArrayBuffer !== 'undefined') {
var src = new ArrayBuffer(5);
var dst = new ArrayBuffer(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});

it("should deeply copy an array into an existing array", function() {
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
Expand Down