ES6: Array.prototype.slice and friends should use ToLength instead of ToUint32
Defines a new --harmony-tolength flag, and a ToLengthFlagged() runtime function, that is used where ES6 requires ToLength(), but a pre-ES6 conversion existed before. When the flag is disabled, the function uses TO_UINT32(), which is the pre-ES6 behaviour. When the flag enabled, the ES6-compliant ToLength() conversion is used. Based on a patch initially from Diego Pino <dpino@igalia.com> BUG=v8:3087 LOG=Y Review URL: https://codereview.chromium.org/1309243003 Cr-Commit-Position: refs/heads/master@{#30772}
This commit is contained in:
parent
0d017282d3
commit
d4e1299f16
44
src/array.js
44
src/array.js
@ -395,7 +395,7 @@ function ArrayToString() {
|
||||
|
||||
|
||||
function InnerArrayToLocaleString(array, length) {
|
||||
var len = TO_UINT32(length);
|
||||
var len = TO_LENGTH_OR_UINT32(length);
|
||||
if (len === 0) return "";
|
||||
return Join(array, len, ',', ConvertToLocaleString);
|
||||
}
|
||||
@ -434,7 +434,7 @@ function ArrayJoin(separator) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
|
||||
return InnerArrayJoin(separator, array, length);
|
||||
}
|
||||
@ -463,7 +463,7 @@ function ArrayPop() {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var n = TO_UINT32(array.length);
|
||||
var n = TO_LENGTH_OR_UINT32(array.length);
|
||||
if (n == 0) {
|
||||
array.length = n;
|
||||
return;
|
||||
@ -481,7 +481,7 @@ function ArrayPop() {
|
||||
|
||||
|
||||
function ObservedArrayPush() {
|
||||
var n = TO_UINT32(this.length);
|
||||
var n = TO_LENGTH_OR_UINT32(this.length);
|
||||
var m = %_ArgumentsLength();
|
||||
|
||||
try {
|
||||
@ -509,7 +509,7 @@ function ArrayPush() {
|
||||
return ObservedArrayPush.apply(this, arguments);
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var n = TO_UINT32(array.length);
|
||||
var n = TO_LENGTH_OR_UINT32(array.length);
|
||||
var m = %_ArgumentsLength();
|
||||
|
||||
for (var i = 0; i < m; i++) {
|
||||
@ -606,7 +606,7 @@ function ArrayReverse() {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_UINT32(array.length);
|
||||
var len = TO_LENGTH_OR_UINT32(array.length);
|
||||
var isArray = IS_ARRAY(array);
|
||||
|
||||
if (UseSparseVariant(array, len, isArray, len)) {
|
||||
@ -641,7 +641,7 @@ function ArrayShift() {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_UINT32(array.length);
|
||||
var len = TO_LENGTH_OR_UINT32(array.length);
|
||||
|
||||
if (len === 0) {
|
||||
array.length = 0;
|
||||
@ -668,7 +668,7 @@ function ArrayShift() {
|
||||
|
||||
|
||||
function ObservedArrayUnshift() {
|
||||
var len = TO_UINT32(this.length);
|
||||
var len = TO_LENGTH_OR_UINT32(this.length);
|
||||
var num_arguments = %_ArgumentsLength();
|
||||
|
||||
try {
|
||||
@ -695,7 +695,7 @@ function ArrayUnshift(arg1) { // length == 1
|
||||
return ObservedArrayUnshift.apply(this, arguments);
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_UINT32(array.length);
|
||||
var len = TO_LENGTH_OR_UINT32(array.length);
|
||||
var num_arguments = %_ArgumentsLength();
|
||||
|
||||
if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
|
||||
@ -719,7 +719,7 @@ function ArraySlice(start, end) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_UINT32(array.length);
|
||||
var len = TO_LENGTH_OR_UINT32(array.length);
|
||||
var start_i = TO_INTEGER(start);
|
||||
var end_i = len;
|
||||
|
||||
@ -790,7 +790,7 @@ function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
|
||||
|
||||
function ObservedArraySplice(start, delete_count) {
|
||||
var num_arguments = %_ArgumentsLength();
|
||||
var len = TO_UINT32(this.length);
|
||||
var len = TO_LENGTH_OR_UINT32(this.length);
|
||||
var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
|
||||
var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
|
||||
start_i);
|
||||
@ -837,7 +837,7 @@ function ArraySplice(start, delete_count) {
|
||||
|
||||
var num_arguments = %_ArgumentsLength();
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_UINT32(array.length);
|
||||
var len = TO_LENGTH_OR_UINT32(array.length);
|
||||
var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
|
||||
var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
|
||||
start_i);
|
||||
@ -1173,7 +1173,7 @@ function ArraySort(comparefn) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArraySort(array, length, comparefn);
|
||||
}
|
||||
|
||||
@ -1207,7 +1207,7 @@ function ArrayFilter(f, receiver) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
var accumulator = InnerArrayFilter(f, receiver, array, length);
|
||||
var result = new GlobalArray();
|
||||
%MoveArrayContents(accumulator, result);
|
||||
@ -1235,7 +1235,7 @@ function ArrayForEach(f, receiver) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
InnerArrayForEach(f, receiver, array, length);
|
||||
}
|
||||
|
||||
@ -1265,7 +1265,7 @@ function ArraySome(f, receiver) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArraySome(f, receiver, array, length);
|
||||
}
|
||||
|
||||
@ -1292,7 +1292,7 @@ function ArrayEvery(f, receiver) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArrayEvery(f, receiver, array, length);
|
||||
}
|
||||
|
||||
@ -1321,7 +1321,7 @@ function ArrayMap(f, receiver) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
var accumulator = InnerArrayMap(f, receiver, array, length);
|
||||
var result = new GlobalArray();
|
||||
%MoveArrayContents(accumulator, result);
|
||||
@ -1390,7 +1390,7 @@ function InnerArrayIndexOf(array, element, index, length) {
|
||||
function ArrayIndexOf(element, index) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
|
||||
|
||||
var length = TO_UINT32(this.length);
|
||||
var length = TO_LENGTH_OR_UINT32(this.length);
|
||||
return InnerArrayIndexOf(this, element, index, length);
|
||||
}
|
||||
|
||||
@ -1448,7 +1448,7 @@ function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
|
||||
function ArrayLastIndexOf(element, index) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
|
||||
|
||||
var length = TO_UINT32(this.length);
|
||||
var length = TO_LENGTH_OR_UINT32(this.length);
|
||||
return InnerArrayLastIndexOf(this, element, index, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
@ -1490,7 +1490,7 @@ function ArrayReduce(callback, current) {
|
||||
// Pull out the length so that modifications to the length in the
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArrayReduce(callback, current, array, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
@ -1533,7 +1533,7 @@ function ArrayReduceRight(callback, current) {
|
||||
// Pull out the length so that side effects are visible before the
|
||||
// callback function is checked.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArrayReduceRight(callback, current, array, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
|
@ -1860,6 +1860,15 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
|
||||
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_tolength() {
|
||||
Handle<JSObject> builtins(native_context()->builtins());
|
||||
Handle<Object> flag(factory()->ToBoolean(FLAG_harmony_tolength));
|
||||
Runtime::SetObjectProperty(isolate(), builtins,
|
||||
factory()->harmony_tolength_string(), flag,
|
||||
STRICT).Assert();
|
||||
}
|
||||
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_reflect() {
|
||||
if (!FLAG_harmony_reflect) return;
|
||||
|
||||
@ -2542,6 +2551,7 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
"native harmony-concat-spreadable.js", nullptr};
|
||||
static const char* harmony_simd_natives[] = {"native harmony-simd.js",
|
||||
nullptr};
|
||||
static const char* harmony_tolength_natives[] = {nullptr};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -194,6 +194,7 @@ DEFINE_BOOL(legacy_const, true, "legacy semantics for const in sloppy mode")
|
||||
V(harmony_proxies, "harmony proxies") \
|
||||
V(harmony_sloppy_function, "harmony sloppy function block scoping") \
|
||||
V(harmony_unicode_regexps, "harmony unicode regexps") \
|
||||
V(harmony_tolength, "harmony ToLength") \
|
||||
V(harmony_reflect, "harmony Reflect API") \
|
||||
V(harmony_destructuring, "harmony destructuring") \
|
||||
V(harmony_default_parameters, "harmony default parameters") \
|
||||
|
@ -173,7 +173,7 @@ function ArrayFill(value, start, end) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
|
||||
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_UINT32(array.length);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
|
||||
return InnerArrayFill(value, start, end, array, length);
|
||||
}
|
||||
|
@ -258,6 +258,7 @@ namespace internal {
|
||||
V(multiline_string, "multiline") \
|
||||
V(sticky_string, "sticky") \
|
||||
V(unicode_string, "unicode") \
|
||||
V(harmony_tolength_string, "harmony_tolength") \
|
||||
V(input_string, "input") \
|
||||
V(index_string, "index") \
|
||||
V(last_index_string, "lastIndex") \
|
||||
|
@ -147,6 +147,7 @@ macro TO_INTEGER_FOR_SIDE_EFFECT(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToNumber(
|
||||
macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(arg)));
|
||||
macro TO_INT32(arg) = (arg | 0);
|
||||
macro TO_UINT32(arg) = (arg >>> 0);
|
||||
macro TO_LENGTH_OR_UINT32(arg) = (harmony_tolength ? $toLength(arg) : TO_UINT32(arg));
|
||||
macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : $nonStringToString(arg));
|
||||
macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : $nonNumberToNumber(arg));
|
||||
macro TO_OBJECT(arg) = (%_ToObject(arg));
|
||||
|
@ -24,6 +24,8 @@ var $toPositiveInteger;
|
||||
var $toPrimitive;
|
||||
var $toString;
|
||||
|
||||
var harmony_tolength = false;
|
||||
|
||||
(function(global, utils) {
|
||||
|
||||
%CheckIsBootstrapping();
|
||||
|
208
test/mjsunit/harmony/array-length.js
Normal file
208
test/mjsunit/harmony/array-length.js
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Flags: --harmony-tolength
|
||||
|
||||
// Test array functions do not cause infinite loops when length is negative,
|
||||
// max_value, etc.
|
||||
|
||||
// ArrayToString
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.toString.call(o);
|
||||
assertEquals("[object Object]", result);
|
||||
|
||||
// ArrayToLocaleString
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.toLocaleString.call(o);
|
||||
assertEquals("", result);
|
||||
|
||||
// ArrayJoin
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.join.call(o);
|
||||
assertEquals(0, result.length);
|
||||
|
||||
// ArrayPush
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.push.call(o, 1);
|
||||
assertEquals(1, o.length);
|
||||
assertEquals(1, o[0]);
|
||||
|
||||
var o = { length: Number.MAX_VALUE };
|
||||
Array.prototype.push.call(o, 1);
|
||||
assertEquals(o.length, Number.MAX_SAFE_INTEGER + 1);
|
||||
assertEquals(1, o[Number.MAX_SAFE_INTEGER]);
|
||||
|
||||
Array.prototype.push.call(o, 2);
|
||||
assertEquals(o.length, Number.MAX_SAFE_INTEGER + 1);
|
||||
assertEquals(2, o[Number.MAX_SAFE_INTEGER]);
|
||||
|
||||
// ArrayPop
|
||||
|
||||
var o = { length: 0 };
|
||||
Array.prototype.pop.call(o);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.pop.call(o);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: Number.MAX_VALUE };
|
||||
Array.prototype.pop.call(o);
|
||||
assertEquals(o.length, Number.MAX_SAFE_INTEGER - 1);
|
||||
|
||||
// ArrayReverse
|
||||
|
||||
var o = { 0: 'foo', length: Number.MIN_VALUE }
|
||||
var result = Array.prototype.reverse.call(o);
|
||||
assertEquals('object', typeof(result));
|
||||
assertEquals(Number.MIN_VALUE, result.length);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayShift
|
||||
|
||||
var o = { 0: "foo", length: Number.MIN_VALUE }
|
||||
var result = Array.prototype.shift.call(o);
|
||||
assertEquals(undefined, result);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
// ArrayUnshift
|
||||
|
||||
var o = { length: 0 };
|
||||
Array.prototype.unshift.call(o);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: 0 };
|
||||
Array.prototype.unshift.call(o, 'foo');
|
||||
assertEquals('foo', o[0]);
|
||||
assertEquals(1, o.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.unshift.call(o);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.unshift.call(o, 'foo');
|
||||
assertEquals('foo', o[0]);
|
||||
assertEquals(1, o.length);
|
||||
|
||||
// ArraySplice
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.splice.call(o);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.splice.call(o, 0, 10, ['foo']);
|
||||
assertEquals(['foo'], o[0]);
|
||||
assertEquals(1, o.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.splice.call(o, -1);
|
||||
assertEquals(0, o.length);
|
||||
|
||||
var o = { length: Number.MAX_SAFE_INTEGER };
|
||||
Array.prototype.splice.call(o, -1);
|
||||
assertEquals(Number.MAX_SAFE_INTEGER - 1, o.length);
|
||||
|
||||
// ArraySlice
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.slice.call(o);
|
||||
assertEquals(0, result.length);
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.slice.call(o, Number.MAX_VALUE);
|
||||
assertEquals(0, result.length);
|
||||
|
||||
var o = { length: Number.MAX_VALUE };
|
||||
var result = Array.prototype.slice.call(o, Number.MAX_VALUE - 1);
|
||||
assertEquals(0, result.length);
|
||||
|
||||
// ArrayIndexOf
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.indexOf.call(o);
|
||||
assertEquals(-1, result);
|
||||
|
||||
var o = { length: Number.MAX_SAFE_INTEGER }
|
||||
o[Number.MAX_SAFE_INTEGER - 1] = "foo"
|
||||
var result = Array.prototype.indexOf.call(o,
|
||||
"foo", Number.MAX_SAFE_INTEGER - 2);
|
||||
assertEquals(Number.MAX_SAFE_INTEGER - 1, result);
|
||||
|
||||
var o = { length: Number.MAX_SAFE_INTEGER };
|
||||
o[Number.MAX_SAFE_INTEGER - 1] = "foo";
|
||||
var result = Array.prototype.indexOf.call(o, "foo", -1);
|
||||
assertEquals(Number.MAX_SAFE_INTEGER - 1, result);
|
||||
|
||||
// ArrayLastIndexOf
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
var result = Array.prototype.lastIndexOf.call(o);
|
||||
assertEquals(-1, result);
|
||||
|
||||
var o = { length: Number.MAX_SAFE_INTEGER }
|
||||
o[Number.MAX_SAFE_INTEGER - 1] = "foo"
|
||||
var result = Array.prototype.lastIndexOf.call(o,
|
||||
"foo", Number.MAX_SAFE_INTEGER);
|
||||
assertEquals(Number.MAX_SAFE_INTEGER - 1, result);
|
||||
|
||||
var o = { length: Number.MAX_SAFE_INTEGER };
|
||||
o[Number.MAX_SAFE_INTEGER - 1] = "foo";
|
||||
var result = Array.prototype.lastIndexOf.call(o, "foo", -1);
|
||||
assertEquals(Number.MAX_SAFE_INTEGER - 1, result);
|
||||
|
||||
// ArrayFilter
|
||||
|
||||
var func = function(v) { return v; }
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.filter.call(o, func);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayForEach
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.forEach.call(o, func);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArraySome
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.some.call(o, func);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayEvery
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.every.call(o, func);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayMap
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.map.call(o, func);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayReduce
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.reduce.call(o, func, 0);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayReduceRight
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.reduceRight.call(o, func, 0);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
||||
|
||||
// ArrayFill
|
||||
|
||||
var o = { length: Number.MIN_VALUE };
|
||||
Array.prototype.fill(o, 0);
|
||||
assertEquals(Number.MIN_VALUE, o.length);
|
Loading…
Reference in New Issue
Block a user