29c1eab92e
Before ES2015, the ES spec had a [[Class]] internal slot for all objects, which Object.prototype.toString() would use to figure the returned string. Post-ES2015, the [[Class]] slot was removed in spec for all objects, with the @@toStringTag well-known symbol the proper way to change Object.prototype.toString() output. At the time, spec-identical handling without the use of [[Class]] was implemented in V8 for all objects other than API objects, where issues with the Web IDL spec [1] prevented Blink, and hence V8, to totally migrate to @@toStringTag. However, since 2016 [2] Blink has been setting @@toStringTag on API class prototypes to manage the Object.prototype.toString() output, so the legacy [[Class]] handling in V8 has not been necessary for the past couple of years. This CL removes the remaining legacy [[Class]] handling in Object.prototype.toString(), JSReceiver::class_name(), and GetConstructorName(). However, it does not remove the class_name field in FunctionTemplateInfo, as it is still used for the `name` property of created functions. This CL also cleans up other places in the codebase that still reference [[Class]]. This change should have minimal impact on web-compatibility. For the change to be observable, a script must do one of the following: 1. delete APIConstructor.prototype[Symbol.toStringTag]; 2. Object.setPrototypeOf(apiObject, somethingElse); Before this CL, these changes will not change the apiObject.toString() output. But after this CL, they will make apiObject.toString() show "[object Object]" (in the first case) or the @@toStringTag of the other prototype (in the latter case). However, both are deemed unlikely. @@toStringTag is not well-known feature of JavaScript, nor does it get tampered much on API constructors. In the second case, setting the prototype of an API object would effectly render the object useless, as all its methods (including property getters/setters) would no longer be accessible. Currently, @@toStringTag-based API object branding is not yet implemented by other browsers. This V8 bug in particular has been an impediment to standardizing toString behavior. Fixing this bug will unblock [3] and lead to a better Web IDL spec, and better toString() compatibility for all. [1]: https://www.w3.org/Bugs/Public/show_bug.cgi?id=28244 [2]: https://crrev.com/909c0d7d5a53c8526ded351683c65ea7d17531d4 [3]: https://github.com/heycam/webidl/pull/357 Bug: chromium:793406 Cq-Include-Trybots: luci.chromium.try:linux-rel Change-Id: Iceded24e37afa2646ec385d5018909f55b177f93 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2146996 Commit-Queue: Timothy Gu <timothygu@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#67327}
337 lines
9.2 KiB
JavaScript
337 lines
9.2 KiB
JavaScript
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
(function copyWithinArity() {
|
|
assertEquals(Array.prototype.copyWithin.length, 2);
|
|
})();
|
|
|
|
|
|
(function copyWithinTargetAndStart() {
|
|
// works with two arguemnts
|
|
assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3));
|
|
assertArrayEquals([1, 4, 5, 4, 5], [1, 2, 3, 4, 5].copyWithin(1, 3));
|
|
assertArrayEquals([1, 3, 4, 5, 5], [1, 2, 3, 4, 5].copyWithin(1, 2));
|
|
assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(2, 2));
|
|
})();
|
|
|
|
|
|
(function copyWithinTargetStartAndEnd() {
|
|
// works with three arguments
|
|
assertArrayEquals([1, 2, 3, 4, 5].copyWithin(0, 3, 4), [4, 2, 3, 4, 5]);
|
|
assertArrayEquals([1, 2, 3, 4, 5].copyWithin(1, 3, 4), [1, 4, 3, 4, 5]);
|
|
assertArrayEquals([1, 2, 3, 4, 5].copyWithin(1, 2, 4), [1, 3, 4, 4, 5]);
|
|
})();
|
|
|
|
|
|
(function copyWithinNegativeRelativeOffsets() {
|
|
// works with negative arguments
|
|
assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, -2));
|
|
assertArrayEquals([4, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, -2, -1));
|
|
assertArrayEquals([1, 3, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3, -2));
|
|
assertArrayEquals([1, 3, 4, 4, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3, -1));
|
|
assertArrayEquals([1, 3, 4, 5, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3));
|
|
// test with arguments equal to -this.length
|
|
assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-5, 0));
|
|
})();
|
|
|
|
|
|
(function copyWithinArrayLikeValues() {
|
|
// works with array-like values
|
|
var args = (function () { return arguments; }(1, 2, 3));
|
|
Array.prototype.copyWithin.call(args, -2, 0);
|
|
assertArrayEquals([1, 1, 2], Array.prototype.slice.call(args));
|
|
|
|
// Object.prototype.toString branding does not change
|
|
assertArrayEquals("[object Arguments]", Object.prototype.toString.call(args));
|
|
})();
|
|
|
|
|
|
(function copyWithinNullThis() {
|
|
// throws on null/undefined values
|
|
assertThrows(function() {
|
|
return Array.prototype.copyWithin.call(null, 0, 3);
|
|
}, TypeError);
|
|
})();
|
|
|
|
|
|
(function copyWithinUndefinedThis() {
|
|
assertThrows(function() {
|
|
return Array.prototype.copyWithin.call(undefined, 0, 3);
|
|
}, TypeError);
|
|
})();
|
|
|
|
|
|
// TODO(caitp): indexed properties of String are read-only and setting them
|
|
// should throw in strict mode. See bug v8:4042
|
|
// (function copyWithinStringThis() {
|
|
// // test with this value as string
|
|
// assertThrows(function() {
|
|
// return Array.prototype.copyWithin.call("hello world", 0, 3);
|
|
// }, TypeError);
|
|
// })();
|
|
|
|
|
|
(function copyWithinNumberThis() {
|
|
// test with this value as number
|
|
assertEquals(34, Array.prototype.copyWithin.call(34, 0, 3).valueOf());
|
|
})();
|
|
|
|
|
|
(function copyWithinSymbolThis() {
|
|
// test with this value as number
|
|
var sym = Symbol("test");
|
|
assertEquals(sym, Array.prototype.copyWithin.call(sym, 0, 3).valueOf());
|
|
})();
|
|
|
|
|
|
(function copyyWithinTypedArray() {
|
|
// test with this value as TypedArray
|
|
var buffer = new ArrayBuffer(16);
|
|
var int32View = new Int32Array(buffer);
|
|
for (var i=0; i<int32View.length; i++) {
|
|
int32View[i] = i*2;
|
|
}
|
|
assertArrayEquals(new Int32Array([2, 4, 6, 6]),
|
|
Array.prototype.copyWithin.call(int32View, 0, 1));
|
|
})();
|
|
|
|
|
|
(function copyWithinSloppyArguments() {
|
|
// if arguments object is sloppy, copyWithin must move the arguments around
|
|
function f(a, b, c, d, e) {
|
|
[].copyWithin.call(arguments, 1, 3);
|
|
return [a, b, c, d, e];
|
|
}
|
|
assertArrayEquals([1, 4, 5, 4, 5], f(1, 2, 3, 4, 5));
|
|
})();
|
|
|
|
|
|
(function copyWithinStartLessThanTarget() {
|
|
// test with target > start on 2 arguments
|
|
assertArrayEquals([1, 2, 3, 1, 2], [1, 2, 3, 4, 5].copyWithin(3, 0));
|
|
|
|
// test with target > start on 3 arguments
|
|
assertArrayEquals([1, 2, 3, 1, 2], [1, 2, 3, 4, 5].copyWithin(3, 0, 4));
|
|
})();
|
|
|
|
|
|
(function copyWithinArrayWithHoles() {
|
|
// test on array with holes
|
|
var arr = new Array(6);
|
|
for (var i = 0; i < arr.length; i += 2) {
|
|
arr[i] = i;
|
|
}
|
|
assertArrayEquals([, 4, , , 4, , ], arr.copyWithin(0, 3));
|
|
})();
|
|
|
|
|
|
(function copyWithinArrayLikeWithHoles() {
|
|
// test on array-like object with holes
|
|
assertArrayEquals({
|
|
length: 6,
|
|
1: 4,
|
|
4: 4
|
|
}, Array.prototype.copyWithin.call({
|
|
length: 6,
|
|
0: 0,
|
|
2: 2,
|
|
4: 4
|
|
}, 0, 3));
|
|
})();
|
|
|
|
|
|
(function copyWithinNonIntegerRelativeOffsets() {
|
|
// test on fractional arguments
|
|
assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0.2, 3.9));
|
|
})();
|
|
|
|
|
|
(function copyWithinNegativeZeroTarget() {
|
|
// test with -0
|
|
assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-0, 3));
|
|
})();
|
|
|
|
|
|
(function copyWithinTargetOutsideStart() {
|
|
// test with arguments more than this.length
|
|
assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 7));
|
|
|
|
// test with arguments less than -this.length
|
|
assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-7, 0));
|
|
})();
|
|
|
|
|
|
(function copyWithinEmptyArray() {
|
|
// test on empty array
|
|
assertArrayEquals([], [].copyWithin(0, 3));
|
|
})();
|
|
|
|
|
|
(function copyWithinTargetCutOff() {
|
|
// test with target range being shorter than end - start
|
|
assertArrayEquals([1, 2, 2, 3, 4], [1, 2, 3, 4, 5].copyWithin(2, 1, 4));
|
|
})();
|
|
|
|
|
|
(function copyWithinOverlappingRanges() {
|
|
// test overlapping ranges
|
|
var arr = [1, 2, 3, 4, 5];
|
|
arr.copyWithin(2, 1, 4);
|
|
assertArrayEquals([1, 2, 2, 2, 3], arr.copyWithin(2, 1, 4));
|
|
})();
|
|
|
|
|
|
(function copyWithinStrictDelete() {
|
|
// check that [[Delete]] is strict (non-extensible via freeze)
|
|
assertThrows(function() {
|
|
return Object.freeze([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
|
|
}, TypeError);
|
|
|
|
// check that [[Delete]] is strict (non-extensible via seal)
|
|
assertThrows(function() {
|
|
return Object.seal([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
|
|
}, TypeError);
|
|
|
|
// check that [[Delete]] is strict (non-extensible via preventExtensions)
|
|
assertThrows(function() {
|
|
return Object.preventExtensions([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
|
|
}, TypeError);
|
|
})();
|
|
|
|
|
|
(function copyWithinStrictSet() {
|
|
// check that [[Set]] is strict (non-extensible via freeze)
|
|
assertThrows(function() {
|
|
return Object.freeze([1, 2, 3, 4, 5]).copyWithin(0, 3);
|
|
}, TypeError);
|
|
|
|
// check that [[Set]] is strict (non-extensible via seal)
|
|
assertThrows(function() {
|
|
return Object.seal([, 2, 3, 4, 5]).copyWithin(0, 3);
|
|
}, TypeError);
|
|
|
|
// check that [[Set]] is strict (non-extensible via preventExtensions)
|
|
assertThrows(function() {
|
|
return Object.preventExtensions([ , 2, 3, 4, 5]).copyWithin(0, 3);
|
|
}, TypeError);
|
|
})();
|
|
|
|
|
|
(function copyWithinSetterThrows() {
|
|
function Boom() {}
|
|
// test if we throw in between
|
|
var arr = Object.defineProperty([1, 2, 3, 4, 5], 1, {
|
|
set: function () {
|
|
throw new Boom();
|
|
}
|
|
});
|
|
|
|
assertThrows(function() {
|
|
return arr.copyWithin(1, 3);
|
|
}, Boom);
|
|
|
|
assertArrayEquals([1, , 3, 4, 5], arr);
|
|
})();
|
|
|
|
|
|
(function copyWithinDefaultEnd() {
|
|
// undefined as third argument
|
|
assertArrayEquals(
|
|
[4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3, undefined));
|
|
})();
|
|
|
|
|
|
(function copyWithinGetLengthOnce() {
|
|
// test that this.length is called only once
|
|
var count = 0;
|
|
var arr = Object.defineProperty({ 0: 1, 1: 2, 2: 3, 3: 4, 4: 5 }, "length", {
|
|
get: function () {
|
|
count++;
|
|
return 5;
|
|
}
|
|
});
|
|
Array.prototype.copyWithin.call(arr, 1, 3);
|
|
assertEquals(1, count);
|
|
|
|
Array.prototype.copyWithin.call(arr, 1, 3, 4);
|
|
assertEquals(2, count);
|
|
})();
|
|
|
|
|
|
(function copyWithinLargeArray() {
|
|
var large = 10000;
|
|
|
|
// test on a large array
|
|
var arr = new Array(large);
|
|
assertArrayEquals(arr, arr.copyWithin(45, 9000));
|
|
|
|
var expected = new Array(large);
|
|
// test on floating point numbers
|
|
for (var i = 0; i < large; i++) {
|
|
arr[i] = Math.random();
|
|
expected[i] = arr[i];
|
|
if (i >= 9000) {
|
|
expected[(i - 9000) + 45] = arr[i];
|
|
}
|
|
}
|
|
assertArrayEquals(expected, arr.copyWithin(45, 9000));
|
|
|
|
// test on array of objects
|
|
for (var i = 0; i < large; i++) {
|
|
arr[i] = { num: Math.random() };
|
|
} + 45
|
|
arr.copyWithin(45, 9000);
|
|
|
|
// test copied by reference
|
|
for (var i = 9000; i < large; ++i) {
|
|
assertSame(arr[(i - 9000) + 45], arr[i]);
|
|
}
|
|
|
|
// test array length remains same
|
|
assertEquals(large, arr.length);
|
|
})();
|
|
|
|
|
|
(function copyWithinSuperLargeLength() {
|
|
// 2^53 - 1 is the maximum value returned from ToLength()
|
|
var large = Math.pow(2, 53) - 1;
|
|
var object = { length: large };
|
|
|
|
// Initialize last 10 entries
|
|
for (var i = 1; i <= 10; ++i) {
|
|
object[(large - 11) + i] = { num: i };
|
|
}
|
|
|
|
Array.prototype.copyWithin.call(object, 1, large - 10);
|
|
|
|
// Test copied values
|
|
for (var i = 1; i <= 10; ++i) {
|
|
var old_ref = object[(large - 11) + i];
|
|
var new_ref = object[i];
|
|
assertSame(old_ref, new_ref);
|
|
assertSame(new_ref.num, i);
|
|
}
|
|
|
|
// Assert length has not changed
|
|
assertEquals(large, object.length);
|
|
})();
|
|
|
|
|
|
(function copyWithinNullEnd() {
|
|
// test null on third argument is converted to +0
|
|
assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3, null));
|
|
})();
|
|
|
|
|
|
(function copyWithinElementsInObjectsPrototype() {
|
|
// tamper the global Object prototype and test this works
|
|
Object.prototype[2] = 1;
|
|
assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3));
|
|
delete Object.prototype[2];
|
|
|
|
Object.prototype[3] = "FAKE";
|
|
assertArrayEquals(["FAKE", 5, 3, "FAKE", 5], [1, 2, 3, , 5].copyWithin(0, 3));
|
|
delete Object.prototype[3];
|
|
})();
|