Commit 6c61c8aa authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

Transition to frozen/sealed elements from Smi/Double elements

When applying Object.seal(), Object.freeze() to Smi, Double elements
kind, it will transition to Object elements kind first then to new
frozen, sealed elements kind accordingly.
Also, add more mjsunit.

Bug: v8:6831
Change-Id: I454b42d7eb329b03e20245896641eb6c1a87831d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1662657
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62457}
parent a7189c43
......@@ -3755,6 +3755,21 @@ template void JSObject::ApplyAttributesToDictionary(
Isolate* isolate, ReadOnlyRoots roots, Handle<NumberDictionary> dictionary,
const PropertyAttributes attributes);
Handle<NumberDictionary> CreateElementDictionary(Isolate* isolate,
Handle<JSObject> object) {
Handle<NumberDictionary> new_element_dictionary;
if (!object->HasTypedArrayElements() && !object->HasDictionaryElements() &&
!object->HasSlowStringWrapperElements()) {
int length = object->IsJSArray()
? Smi::ToInt(Handle<JSArray>::cast(object)->length())
: object->elements().length();
new_element_dictionary =
length == 0 ? isolate->factory()->empty_slow_element_dictionary()
: object->GetElementsAccessor()->Normalize(object);
}
return new_element_dictionary;
}
template <PropertyAttributes attrs>
Maybe<bool> JSObject::PreventExtensionsWithTransition(
Handle<JSObject> object, ShouldThrow should_throw) {
......@@ -3775,10 +3790,12 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
}
if (attrs == NONE && !object->map().is_extensible()) return Just(true);
ElementsKind old_elements_kind = object->map().elements_kind();
if (attrs != FROZEN && IsSealedElementsKind(old_elements_kind))
return Just(true);
if (old_elements_kind == PACKED_FROZEN_ELEMENTS) return Just(true);
{
ElementsKind old_elements_kind = object->map().elements_kind();
if (attrs != FROZEN && IsSealedElementsKind(old_elements_kind))
return Just(true);
if (old_elements_kind == PACKED_FROZEN_ELEMENTS) return Just(true);
}
if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object);
......@@ -3807,17 +3824,6 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
RETURN_FAILURE(isolate, should_throw, NewTypeError(message));
}
Handle<NumberDictionary> new_element_dictionary;
if (!object->HasTypedArrayElements() && !object->HasDictionaryElements() &&
!object->HasSlowStringWrapperElements()) {
int length = object->IsJSArray()
? Smi::ToInt(Handle<JSArray>::cast(object)->length())
: object->elements().length();
new_element_dictionary =
length == 0 ? isolate->factory()->empty_slow_element_dictionary()
: object->GetElementsAccessor()->Normalize(object);
}
Handle<Symbol> transition_marker;
if (attrs == NONE) {
transition_marker = isolate->factory()->nonextensible_symbol();
......@@ -3828,6 +3834,31 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
transition_marker = isolate->factory()->frozen_symbol();
}
// Currently, there are only have sealed/frozen Object element kinds and
// Map::MigrateToMap doesn't handle properties' attributes reconfiguring and
// elements kind change in one go. If seal or freeze with Smi or Double
// elements kind, we will transition to Object elements kind first to make
// sure of valid element access.
if (FLAG_enable_sealed_frozen_elements_kind &&
(attrs == SEALED || attrs == FROZEN)) {
switch (object->map().elements_kind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
JSObject::TransitionElementsKind(object, PACKED_ELEMENTS);
break;
case HOLEY_SMI_ELEMENTS:
case HOLEY_DOUBLE_ELEMENTS:
JSObject::TransitionElementsKind(object, HOLEY_ELEMENTS);
break;
default:
break;
}
}
// Make sure we only use this element dictionary in case we can't transition
// to sealed, frozen elements kind.
Handle<NumberDictionary> new_element_dictionary;
Handle<Map> old_map(object->map(), isolate);
old_map = Map::Update(isolate, old_map);
TransitionsAccessor transitions(isolate, old_map);
......@@ -3839,11 +3870,17 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS ||
transition_map->has_frozen_or_sealed_elements());
DCHECK(!transition_map->is_extensible());
if (!transition_map->has_frozen_or_sealed_elements()) {
new_element_dictionary = CreateElementDictionary(isolate, object);
}
JSObject::MigrateToMap(isolate, object, transition_map);
} else if (transitions.CanHaveMoreTransitions()) {
// Create a new descriptor array with the appropriate property attributes
Handle<Map> new_map = Map::CopyForPreventExtensions(
isolate, old_map, attrs, transition_marker, "CopyForPreventExtensions");
if (!new_map->has_frozen_or_sealed_elements()) {
new_element_dictionary = CreateElementDictionary(isolate, object);
}
JSObject::MigrateToMap(isolate, object, new_map);
} else {
DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
......@@ -3856,6 +3893,8 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
Handle<Map> new_map = Map::Copy(isolate, handle(object->map(), isolate),
"SlowCopyForPreventExtensions");
new_map->set_is_extensible(false);
DCHECK(!new_map->has_frozen_or_sealed_elements());
new_element_dictionary = CreateElementDictionary(isolate, object);
if (!new_element_dictionary.is_null()) {
ElementsKind new_kind =
IsStringWrapperElementsKind(old_map->elements_kind())
......@@ -3882,6 +3921,7 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
}
if (object->map().has_frozen_or_sealed_elements()) {
DCHECK(new_element_dictionary.is_null());
return Just(true);
}
......
......@@ -881,3 +881,473 @@ assertTrue(checkUndefined.apply(this, [...arr]));
assertEquals(a[0], a[1]);
})();
// Test with double elements
// Test packed element array built-in functions with freeze.
function testDoubleFrozenArray1(obj) {
assertTrue(Object.isSealed(obj));
// Verify that the value can't be written
obj1 = new Array(...obj);
var length = obj.length;
for (var i = 0; i < length-1; i++) {
obj[i] = 'new';
assertEquals(obj1[i], obj[i]);
}
// Verify that the length can't be written by builtins.
assertTrue(Array.isArray(obj));
assertThrows(function() { obj.pop(); }, TypeError);
assertThrows(function() { obj.push(); }, TypeError);
assertThrows(function() { obj.shift(); }, TypeError);
assertThrows(function() { obj.unshift(); }, TypeError);
assertThrows(function() { obj.copyWithin(0,0); }, TypeError);
assertThrows(function() { obj.fill(0); }, TypeError);
assertThrows(function() { obj.reverse(); }, TypeError);
assertThrows(function() { obj.sort(); }, TypeError);
assertThrows(function() { obj.splice(0); }, TypeError);
assertThrows(function() { obj.splice(0, 0); }, TypeError);
assertTrue(Object.isFrozen(obj));
// Verify search, filter, iterator
assertEquals(obj.lastIndexOf(1), 2);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertTrue(obj.includes(1));
assertTrue(obj.includes(-1.1));
assertFalse(obj.includes());
assertEquals(obj.find(x => x==0), undefined);
assertEquals(obj.findIndex(x => x==2), 4);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 2);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, 1.1);
assertEquals(iterator.next().value, -1.1);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, 1.1]);
assertEquals(iterator.next().value, [1, -1.1]);
}
obj = new Array(1.1, -1.1, 1, -1, 2);
assertTrue(%HasDoubleElements(obj));
Object.freeze(obj);
testDoubleFrozenArray1(obj);
// Verify change from sealed to frozen
obj = new Array(1.1, -1.1, 1, -1, 2);
assertTrue(%HasDoubleElements(obj));
Object.seal(obj);
Object.freeze(obj);
assertTrue(Object.isSealed(obj));
testDoubleFrozenArray1(obj);
// Verify change from non-extensible to frozen
obj = new Array(1.1, -1.1, 1, -1, 2);
assertTrue(%HasDoubleElements(obj));
Object.preventExtensions(obj);
Object.freeze(obj);
assertTrue(Object.isSealed(obj));
testDoubleFrozenArray1(obj);
// Verify flat, map, slice, flatMap, join, reduce, reduceRight for frozen packed array
function testDoubleFrozenArray2(arr) {
assertTrue(Object.isFrozen(arr));
assertTrue(Array.isArray(arr));
assertEquals(arr.map(x => [x]), [[1], [1.1], [0]]);
assertEquals(arr.flatMap(x => [x]), arr);
assertEquals(arr.flat(), arr);
assertEquals(arr.join('-'), "1-1.1-0");
const reducer = (accumulator, currentValue) => accumulator + currentValue;
assertEquals(arr.reduce(reducer), 2.1);
assertEquals(arr.reduceRight(reducer), 2.1);
assertEquals(arr.slice(0, 1), [1]);
}
var arr1 = new Array(1, 1.1, 0);
assertTrue(%HasDoubleElements(arr1));
Object.freeze(arr1);
testDoubleFrozenArray2(arr1);
// Verify change from sealed to frozen
var arr1 = new Array(1, 1.1, 0);
assertTrue(%HasDoubleElements(arr1));
Object.seal(arr1);
Object.freeze(arr1);
testDoubleFrozenArray2(arr1);
// Verify change from non-extensible to frozen
var arr1 = new Array(1, 1.1, 0);
assertTrue(%HasDoubleElements(arr1));
Object.preventExtensions(arr1);
Object.freeze(arr1);
testDoubleFrozenArray2(arr1);
// Test regression with Object.defineProperty
var obj = [];
obj.propertyA = 42;
obj[0] = 1.1;
Object.freeze(obj);
assertThrows(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
});
}, TypeError);
assertEquals(42, obj.propertyA);
assertThrows(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
writable: false,
});
}, TypeError);
assertDoesNotThrow(function() {obj.propertyA = 2;});
assertEquals(obj.propertyA, 42);
assertThrows(function() {
Object.defineProperty(obj, 'abc', {
value: obj,
});
}, TypeError);
// Regression test with simple array
var arr = [1.1];
Object.freeze(arr);
arr[0] = 1;
assertEquals(arr[0], 1.1);
// Test regression Array.concat with double
var arr = [1.1];
Object.freeze(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, 0.5]);
Object.freeze(arr);
arr = arr.concat([1.5, 'b']);
assertEquals(arr, [1.1, 0.5, 1.5, 'b']);
// Regression test with change length
var arr = [1.1, 0];
Object.freeze(arr);
assertEquals(arr.length, 2);
arr.length = 3;
assertEquals(arr.length, 2);
arr[2] = 'c';
assertEquals(arr[2], undefined);
arr.length = 1;
assertEquals(arr.length, 2);
// Start testing with holey array
// Test holey element array built-in functions with freeze.
function testHoleyDoubleFrozenArray1(obj) {
assertTrue(Object.isSealed(obj));
// Verify that the value can't be written
obj1 = new Array(...obj);
var length = obj.length;
for (var i = 0; i < length-1; i++) {
obj[i] = 'new';
assertEquals(obj1[i], obj[i]);
}
// Verify that the length can't be written by builtins.
assertTrue(Array.isArray(obj));
assertThrows(function() { obj.pop(); }, TypeError);
assertThrows(function() { obj.push(); }, TypeError);
assertThrows(function() { obj.shift(); }, TypeError);
assertThrows(function() { obj.unshift(); }, TypeError);
assertThrows(function() { obj.copyWithin(0,0); }, TypeError);
assertThrows(function() { obj.fill(0); }, TypeError);
assertThrows(function() { obj.reverse(); }, TypeError);
assertThrows(function() { obj.sort(); }, TypeError);
assertThrows(function() { obj.splice(0); }, TypeError);
assertThrows(function() { obj.splice(0, 0); }, TypeError);
assertTrue(Object.isFrozen(obj));
// Verify search, filter, iterator
assertEquals(obj.lastIndexOf(1), 2);
assertEquals(obj.indexOf(1.1), 5);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertTrue(obj.includes(undefined));
assertFalse(obj.includes(NaN));
assertTrue(obj.includes());
assertEquals(obj.find(x => x==2), undefined);
assertEquals(obj.findIndex(x => x==1.1), 5);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 2);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, -1.1);
assertEquals(iterator.next().value, 0);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, -1.1]);
assertEquals(iterator.next().value, [1, 0]);
}
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.freeze(obj);
testHoleyDoubleFrozenArray1(obj);
// Verify change from sealed to frozen
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.seal(obj);
Object.freeze(obj);
assertTrue(Object.isSealed(obj));
testHoleyDoubleFrozenArray1(obj);
// Verify change from non-extensible to frozen
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.preventExtensions(obj);
Object.freeze(obj);
assertTrue(Object.isSealed(obj));
testHoleyDoubleFrozenArray1(obj);
// Verify flat, map, slice, flatMap, join, reduce, reduceRight for frozen packed array
function testHoleyDoubleFrozenArray2(arr) {
assertTrue(Object.isFrozen(arr));
assertTrue(Array.isArray(arr));
assertEquals(arr.map(x => [x]), [, [1.1], [1], [0]]);
assertEquals(arr.flatMap(x => [x]), [1.1, 1, 0]);
assertEquals(arr.flat(), [1.1, 1, 0]);
assertEquals(arr.join('-'), "-1.1-1-0");
const reducer = (accumulator, currentValue) => accumulator + currentValue;
assertEquals(arr.reduce(reducer), 2.1);
assertEquals(arr.reduceRight(reducer), 2.1);
assertEquals(arr.slice(0, 1), [,]);
assertEquals(arr.slice(1, 2), [1.1]);
}
var arr1 = [, 1.1, 1, 0];
assertTrue(%HasHoleyElements(arr1));
Object.preventExtensions(arr1);
Object.freeze(arr1);
testHoleyDoubleFrozenArray2(arr1);
// Verify change from sealed to frozen
var arr1 = [, 1.1, 1, 0];
assertTrue(%HasHoleyElements(arr1));
Object.seal(arr1);
Object.freeze(arr1);
testHoleyDoubleFrozenArray2(arr1);
// Verify change from non-extensible to frozen
var arr1 = [, 1.1, 1, 0];
assertTrue(%HasHoleyElements(arr1));
Object.preventExtensions(arr1);
Object.freeze(arr1);
testHoleyDoubleFrozenArray2(arr1);
// Test regression with Object.defineProperty
var obj = [1.1, , 0];
obj.propertyA = 42;
obj[0] = true;
Object.freeze(obj);
assertThrows(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
});
}, TypeError);
assertEquals(42, obj.propertyA);
assertThrows(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
writable: false,
});
}, TypeError);
assertDoesNotThrow(function() {obj.propertyA = 2;});
assertEquals(obj.propertyA, 42);
assertThrows(function() {
Object.defineProperty(obj, 'abc', {
value: obj,
});
}, TypeError);
// Regression test with simple holey array
var arr = [, 1.1];
Object.freeze(arr);
arr[1] = 'b';
assertEquals(arr[1], 1.1);
arr[0] = 1;
assertEquals(arr[0], undefined);
// Test regression Array.concat with double
var arr = [1.1, , 0];
Object.freeze(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, , 0, 0.5]);
Object.freeze(arr);
arr = arr.concat([1.5, 'c']);
assertEquals(arr, [1.1, ,0, 0.5, 1.5, 'c']);
// Regression test with change length
var arr = [1.1, ,0];
Object.freeze(arr);
assertEquals(arr.length, 3);
arr.length = 4;
assertEquals(arr.length, 3);
arr[3] = 'c';
assertEquals(arr[2], 0);
assertEquals(arr[3], undefined);
arr.length = 2;
assertEquals(arr.length, 3);
// Change length with holey entries at the end
var arr = [1.1, ,];
Object.freeze(arr);
assertEquals(arr.length, 2);
arr.length = 0;
assertEquals(arr.length, 2);
arr.length = 3;
assertEquals(arr.length, 2);
arr.length = 0;
assertEquals(arr.length, 2);
// Spread with array
var arr = [1.1, 0, -1];
Object.freeze(arr);
var arrSpread = [...arr];
assertEquals(arrSpread.length, arr.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with array-like
function returnArgs() {
return Object.freeze(arguments);
}
var arrLike = returnArgs(1.1, 0, -1);
assertTrue(Object.isFrozen(arrLike));
var arrSpread = [...arrLike];
assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 1.1, 0];
Object.freeze(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
//
// Array.prototype.map
//
(function() {
var a = Object.freeze([0.1,1,2,3,4]);
// Simple use.
var result = [1.1,2,3,4,5];
assertArrayEquals(result, a.map(function(n) { return Number(n) + 1; }));
// Use specified object as this object when calling the function.
var o = { delta: 42 }
result = [42.1,43,44,45,46];
assertArrayEquals(result, a.map(function(n) { return this.delta + Number(n); }, o));
// Modify original array.
b = Object.freeze([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
b.map(function(n, index, array) {
array[index] = Number(n) + 1; return Number(n) + 1;
}));
assertArrayEquals(b, a);
// Only loop through initial part of array and elements are not
// added.
a = Object.freeze([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
a.map(function(n, index, array) { assertThrows(() => { array.push(n) }); return Number(n) + 1; }));
assertArrayEquals([0.1,1,2,3,4], a);
// Respect holes.
a = new Array(20);
a[1] = 1.1;
Object.freeze(a);
a = Object.freeze(a).map(function(n) { return 2*Number(n); });
for (var i in a) {
assertEquals(2.2, a[i]);
assertEquals('1', i);
}
// Skip over missing properties.
a = {
"0": 1.1,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2.2, , 4],
Array.prototype.map.call(Object.freeze(a), function(n) {
received.push(n);
return n * 2;
}));
assertArrayEquals([1.1, 2], received);
// Modify array prototype
a = [1.1, , 2];
received = [];
assertThrows(() => {
Array.prototype.map.call(Object.freeze(a), function(n) {
a.__proto__ = null;
received.push(n);
return n * 2;
});
}, TypeError);
assertArrayEquals([], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
Object.freeze([1.1, 2]).map(function() { a.push(this) }, "");
assertTrue(a[0] !== a[1]);
// Do not create a new object otherwise.
a = [];
Object.freeze([1.1, 2]).map(function() { a.push(this) }, {});
assertSame(a[0], a[1]);
// In strict mode primitive values should not be coerced to an object.
a = [];
Object.freeze([1.1, 1.2]).map(function() { 'use strict'; a.push(this); }, "");
assertEquals("", a[0]);
assertEquals(a[0], a[1]);
})();
......@@ -547,3 +547,388 @@ assertTrue(checkUndefined.apply(this, [...arr]));
assertEquals(a[0], a[1]);
})();
// Test for double element
// Test packed element array built-in functions with preventExtensions.
obj = new Array(-1.1, 0, 1, -1, 1.1);
assertTrue(%HasDoubleElements(obj));
Object.preventExtensions(obj);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
// Verify that the length can't be written by builtins.
assertThrows(function() { obj.push(1); }, TypeError);
assertDoesNotThrow(function() { obj.shift(); });
assertThrows(function() { obj.unshift(1); }, TypeError);
assertThrows(function() { obj.splice(0, 0, 1); }, TypeError);
assertDoesNotThrow(function() {obj.splice(0, 0)});
// Verify search, filter, iterator
obj = new Array(-1.1, 0, 1, -1, 1.1);
assertTrue(%HasDoubleElements(obj));
Object.preventExtensions(obj);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
assertEquals(obj.lastIndexOf(1), 2);
assertEquals(obj.indexOf(1.1), 4);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertFalse(obj.includes(undefined));
assertFalse(obj.includes(NaN));
assertFalse(obj.includes());
assertEquals(obj.find(x => x==0), 0);
assertEquals(obj.findIndex(x => x==1.1), 4);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 2);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, -1.1);
assertEquals(iterator.next().value, 0);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, -1.1]);
assertEquals(iterator.next().value, [1, 0]);
// Verify that the value can be written
var length = obj.length;
for (var i = 0; i < length-1; i++) {
obj[i] = 'new';
assertEquals(obj[i], 'new');
}
// Verify flat, map, flatMap, join, reduce, reduceRight for non-extensible packed array
var arr = [1.1, 0, 1];
assertTrue(%HasDoubleElements(arr));
Object.preventExtensions(arr);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
assertEquals(arr.map(x => [x]), [[1.1], [0], [1]]);
assertEquals(arr.flatMap(x => [x]), arr);
assertEquals(arr.flat(), arr);
assertEquals(arr.join('-'), "1.1-0-1");
assertEquals(arr.reduce(reducer), 2.1);
assertEquals(arr.reduceRight(reducer), 2.1);
assertEquals(arr.slice(0, 1), [1.1]);
// Verify change content of non-extensible packed array
arr.sort();
assertEquals(arr.join(''), "011.1");
arr.reverse();
assertEquals(arr.join(''), "1.110");
arr.copyWithin(0, 1, 2);
assertEquals(arr.join(''),"110");
arr.fill('d');
assertEquals(arr.join(''), "ddd");
arr.pop();
assertEquals(arr.join(''), "dd");
// Regression test with simple array
var arr = [1.1];
Object.preventExtensions(arr);
arr[0] = 'b';
assertEquals(arr[0], 'b');
// Test regression Array.concat with double
var arr = [1.1];
Object.preventExtensions(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, 0.5]);
Object.preventExtensions(arr);
arr = arr.concat([1.5, 'b']);
assertEquals(arr, [1.1, 0.5, 1.5, 'b']);
// Regression test with change length
var arr = [1.1, 0];
Object.preventExtensions(arr);
assertEquals(arr.length, 2);
arr.length = 3;
assertEquals(arr.length, 3);
arr[2] = 'c';
assertEquals(arr[2], undefined);
arr.length = 1;
assertEquals(arr.length, 1);
assertEquals(arr[1], undefined);
// Test for holey array
// Test holey element array built-in functions with preventExtensions.
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasDoubleElements(obj));
Object.preventExtensions(obj);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
// Verify that the length can't be written by builtins.
assertThrows(function() { obj.push(1); }, TypeError);
assertThrows(function() { obj.shift(); }, TypeError);
assertThrows(function() { obj.unshift(1); }, TypeError);
assertThrows(function() { obj.splice(0, 0, 1); }, TypeError);
assertDoesNotThrow(function() {obj.splice(0, 0)});
// Verify search, filter, iterator
obj = [-1.1, 0, 1, ,-1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.preventExtensions(obj);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
assertEquals(obj.lastIndexOf(1), 2);
assertEquals(obj.indexOf(1.1), 5);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertTrue(obj.includes(undefined));
assertFalse(obj.includes(NaN));
assertTrue(obj.includes());
assertEquals(obj.find(x => x==0), 0);
assertEquals(obj.findIndex(x => x==1.1), 5);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 2);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, -1.1);
assertEquals(iterator.next().value, 0);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, -1.1]);
assertEquals(iterator.next().value, [1, 0]);
// Verify that the value can be written
var length = obj.length;
for (var i = 0; i < length-1; i++) {
if (i==3) continue;
obj[i] = 'new';
assertEquals(obj[i], 'new');
}
// Verify flat, map, flatMap, join, reduce, reduceRight for non-extensible holey array
var arr = [, 1.1, 0, 1];
assertTrue(%HasDoubleElements(arr));
Object.preventExtensions(arr);
assertFalse(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertFalse(Object.isExtensible(obj));
assertTrue(Array.isArray(obj));
assertEquals(arr.map(x => [x]), [, [1.1], [0], [1]]);
assertEquals(arr.flatMap(x => [x]), [1.1, 0, 1]);
assertEquals(arr.flat(), [1.1, 0, 1]);
assertEquals(arr.join('-'), "-1.1-0-1");
assertEquals(arr.reduce(reducer1), 2.1);
assertEquals(arr.reduceRight(reducer1), 2.1);
assertEquals(arr.slice(0, 1), [,]);
assertEquals(arr.slice(1, 2), [1.1]);
// Verify change content of non-extensible holey array
assertThrows(function(){arr.sort();}, TypeError);
assertEquals(arr.join(''), "1.101");
assertThrows(function(){arr.reverse();}, TypeError);
assertEquals(arr.join(''), "1.101");
assertThrows(function(){arr.copyWithin(0, 1, 2);}, TypeError);
arr.copyWithin(1, 2, 3);
assertEquals(arr.join(''),"001");
assertThrows(function(){arr.fill('d');}, TypeError);
assertEquals(arr.join(''), "001");
arr.pop();
assertEquals(arr.join(''), "00");
// Regression test with simple holey array
var arr = [, 1.1];
Object.preventExtensions(arr);
arr[1] = 'b';
assertEquals(arr[1], 'b');
arr[0] = 1;
assertEquals(arr[0], undefined);
// Test regression Array.concat with double
var arr = [1.1, , 0];
Object.preventExtensions(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, , 0, 0.5]);
Object.preventExtensions(arr);
arr = arr.concat([1.5, 'c']);
assertEquals(arr, [1.1, , 0, 0.5, 1.5, 'c']);
// Regression test with change length
var arr = [1.1, , 0];
Object.preventExtensions(arr);
assertEquals(arr.length, 3);
arr.length = 4;
assertEquals(arr.length, 4);
arr[3] = 'c';
assertEquals(arr[3], undefined);
arr.length = 2;
assertEquals(arr.length, 2);
assertEquals(arr[2], undefined);
assertEquals(arr.pop(), undefined);
assertEquals(arr.length, 1);
assertEquals(arr[1], undefined);
// Change length with holey entries at the end
var arr = [1.1, ,];
Object.preventExtensions(arr);
assertEquals(arr.length, 2);
arr.length = 0;
assertEquals(arr.length, 0);
arr.length = 3;
assertEquals(arr.length, 3);
arr.length = 0;
assertEquals(arr.length, 0);
// Spread with array
var arr = [1.1, 0, -1];
Object.preventExtensions(arr);
var arrSpread = [...arr];
assertEquals(arrSpread.length, arr.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with array-like
function returnArgs() {
return Object.preventExtensions(arguments);
}
var arrLike = returnArgs(1.1, 0, -1);
assertFalse(Object.isExtensible(arrLike));
var arrSpread = [...arrLike];
assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 1.1, 0];
Object.preventExtensions(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
//
// Array.prototype.map
//
(function() {
var a = Object.preventExtensions([0.1,1,2,3,4]);
// Simple use.
var result = [1.1,2,3,4,5];
assertArrayEquals(result, a.map(function(n) { return Number(n) + 1; }));
// Use specified object as this object when calling the function.
var o = { delta: 42 }
result = [42.1,43,44,45,46];
assertArrayEquals(result, a.map(function(n) { return this.delta + Number(n); }, o));
// Modify original array.
b = Object.preventExtensions([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
b.map(function(n, index, array) {
array[index] = Number(n) + 1; return Number(n) + 1;
}));
assertArrayEquals(b, result);
// Only loop through initial part of array and elements are not
// added.
a = Object.preventExtensions([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
a.map(function(n, index, array) { assertThrows(() => { array.push(n) }); return Number(n) + 1; }));
assertArrayEquals([0.1,1,2,3,4], a);
// Respect holes.
a = new Array(20);
a[1] = 1.1;
Object.preventExtensions(a);
a = Object.preventExtensions(a).map(function(n) { return 2*Number(n); });
for (var i in a) {
assertEquals(2.2, a[i]);
assertEquals('1', i);
}
// Skip over missing properties.
a = {
"0": 1.1,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2.2, , 4],
Array.prototype.map.call(Object.preventExtensions(a), function(n) {
received.push(n);
return n * 2;
}));
assertArrayEquals([1.1, 2], received);
// Modify array prototype
a = [1.1 , 2];
received = [];
assertThrows(() => {
Array.prototype.map.call(Object.preventExtensions(a), function(n) {
a.__proto__ = null;
received.push(n);
return n * 2;
});
}, TypeError);
assertArrayEquals([], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
Object.preventExtensions([1.1, 2]).map(function() { a.push(this) }, "");
assertTrue(a[0] !== a[1]);
// Do not create a new object otherwise.
a = [];
Object.preventExtensions([1.1, 2]).map(function() { a.push(this) }, {});
assertSame(a[0], a[1]);
// In strict mode primitive values should not be coerced to an object.
a = [];
Object.preventExtensions([1.1, 2]).map(function() { 'use strict'; a.push(this); }, "");
assertEquals("", a[0]);
assertEquals(a[0], a[1]);
})();
......@@ -36,7 +36,7 @@ for (var key in non_objects) {
assertSame(non_objects[key], Object.seal(non_objects[key]));
}
// Test that isFrozen always returns true for non-objects
// Test that isSealed always returns true for non-objects
for (var key in non_objects) {
assertTrue(Object.isSealed(non_objects[key]));
}
......@@ -853,3 +853,442 @@ assertTrue(checkUndefined.apply(this, [...arr]));
assertEquals(a[0], a[1]);
})();
// Test with double elements
// Test packed element array built-in functions with seal.
function testDoubleSealedArray1(obj) {
assertTrue(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertTrue(Array.isArray(obj));
// Verify that the length can't be written by builtins.
assertThrows(function() { obj.pop(); }, TypeError);
assertThrows(function() { obj.push(1); }, TypeError);
assertThrows(function() { obj.shift(); }, TypeError);
assertThrows(function() { obj.unshift(1); }, TypeError);
assertThrows(function() { obj.splice(0); }, TypeError);
assertDoesNotThrow(function() { obj.splice(0, 0); });
// Verify search, filter, iterator
assertEquals(obj.lastIndexOf(1), 1);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertTrue(obj.includes(1));
assertTrue(obj.includes(-1.1));
assertFalse(obj.includes());
assertEquals(obj.find(x => x==0), undefined);
assertEquals(obj.findIndex(x => x==2), 3);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 1);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, -1.1);
assertEquals(iterator.next().value, 1);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, -1.1]);
assertEquals(iterator.next().value, [1, 1]);
// Verify that the value can't be written
var length = obj.length;
for (var i = 0; i < length; i++) {
obj[i] = 'new';
assertEquals('new', obj[i]);
}
}
obj = new Array(1.1, -1.1, 1, -1, 2);
assertTrue(%HasDoubleElements(obj));
Object.seal(obj);
testDoubleSealedArray1(obj);
// Verify change from non-extensible to sealed
obj = new Array(1.1, -1.1, 1, -1, 2);
assertTrue(%HasDoubleElements(obj));
Object.preventExtensions(obj);
Object.seal(obj);
assertTrue(Object.isSealed(obj));
testDoubleSealedArray1(obj);
// Verify flat, map, slice, flatMap, join, reduce, reduceRight for sealed packed array
function testDoubleSealedArray2(arr) {
assertTrue(Object.isSealed(arr));
assertTrue(Array.isArray(arr));
assertEquals(arr.map(x => [x]), [[1], [1.1], [0]]);
assertEquals(arr.flatMap(x => [x]), arr);
assertEquals(arr.flat(), arr);
assertEquals(arr.join('-'), "1-1.1-0");
const reducer = (accumulator, currentValue) => accumulator + currentValue;
assertEquals(arr.reduce(reducer), 2.1);
assertEquals(arr.reduceRight(reducer), 2.1);
assertEquals(arr.slice(0, 1), [1]);
}
var arr1 = new Array(1, 1.1, 0);
assertTrue(%HasDoubleElements(arr1));
Object.seal(arr1);
testDoubleSealedArray2(arr1);
// Verify change from non-extensible to sealed
var arr1 = new Array(1, 1.1, 0);
assertTrue(%HasDoubleElements(arr1));
Object.preventExtensions(arr1);
Object.seal(arr1);
testDoubleSealedArray2(arr1);
// Test regression with Object.defineProperty
var obj = [];
obj.propertyA = 42;
obj[0] = 1.1;
Object.seal(obj);
assertDoesNotThrow(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
});
});
assertEquals(obj, obj.propertyA);
assertDoesNotThrow(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
writable: false,
});
});
obj.propertyA = 42;
assertEquals(obj.propertyA, 42);
assertThrows(function() {
Object.defineProperty(obj, 'abc', {
value: obj,
});
}, TypeError);
// Regression test with simple array
var arr = [1.1];
Object.seal(arr);
arr[0] = 1;
assertEquals(arr[0], 1);
// Test regression Array.concat with double
var arr = [1.1];
Object.seal(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, 0.5]);
Object.seal(arr);
arr = arr.concat([1.5, 'b']);
assertEquals(arr, [1.1, 0.5, 1.5, 'b']);
// Regression test with change length
var arr = [1.1, 0];
Object.seal(arr);
assertEquals(arr.length, 2);
arr.length = 3;
assertEquals(arr.length, 3);
arr[2] = 'c';
assertEquals(arr[2], undefined);
arr.length = 1;
assertEquals(arr.length, 2);
// Start testing for holey double element array
// Test holey double element array built-in functions with seal.
function testHoleyDoubleSealedArray1() {
assertTrue(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertTrue(Array.isArray(obj));
// Verify that the length can't be written by builtins.
assertThrows(function() { obj.pop(); }, TypeError);
assertThrows(function() { obj.push(1); }, TypeError);
assertThrows(function() { obj.shift(); }, TypeError);
assertThrows(function() { obj.unshift(1); }, TypeError);
assertThrows(function() { obj.splice(0); }, TypeError);
assertDoesNotThrow(function() { obj.splice(0, 0); });
// Verify search, filter, iterator
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.seal(obj);
assertTrue(Object.isSealed(obj));
assertFalse(Object.isFrozen(obj));
assertTrue(Array.isArray(obj));
assertEquals(obj.lastIndexOf(1), 2);
assertEquals(obj.indexOf(1.1), 5);
assertEquals(obj.indexOf(undefined), -1);
assertFalse(obj.includes(Symbol("test")));
assertTrue(obj.includes(undefined));
assertFalse(obj.includes(NaN));
assertTrue(obj.includes());
assertEquals(obj.find(x => x==0), 0);
assertEquals(obj.findIndex(x => x==1.1), 5);
assertFalse(obj.some(x => typeof x == 'symbol'));
assertFalse(obj.every(x => x == -1));
var filteredArray = obj.filter(e => typeof e == "symbol");
assertEquals(filteredArray.length, 0);
assertEquals(obj.map(x => x), obj);
var countPositiveNumber = 0;
obj.forEach(function(item, index) {
if (item === 1) {
countPositiveNumber++;
assertEquals(index, 2);
}
});
assertEquals(countPositiveNumber, 1);
assertEquals(obj.length, obj.concat([]).length);
var iterator = obj.values();
assertEquals(iterator.next().value, -1.1);
assertEquals(iterator.next().value, 0);
var iterator = obj.keys();
assertEquals(iterator.next().value, 0);
assertEquals(iterator.next().value, 1);
var iterator = obj.entries();
assertEquals(iterator.next().value, [0, -1.1]);
assertEquals(iterator.next().value, [1, 0]);
// Verify that the value can be written
var length = obj.length;
for (var i = 0; i < length; i++) {
if (i==3) continue;
obj[i] = 'new';
assertEquals(obj[i], 'new');
}
};
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.seal(obj);
testHoleyDoubleSealedArray1(obj);
// Verify change from non-extensible to sealed
obj = [-1.1, 0, 1, , -1, 1.1];
assertTrue(%HasHoleyElements(obj));
Object.preventExtensions(obj);
Object.seal(obj);
assertTrue(Object.isSealed(obj));
testHoleyDoubleSealedArray1(obj);
// Verify flat, map, slice, flatMap, join, reduce, reduceRight for sealed packed array
function testHoleyDoubleSealedArray2(arr) {
assertTrue(Object.isSealed(arr));
assertTrue(Array.isArray(arr));
assertEquals(arr.map(x => [x]), [, [1.1], [1], [0]]);
assertEquals(arr.flatMap(x => [x]), [1.1, 1, 0]);
assertEquals(arr.flat(), [1.1, 1, 0]);
assertEquals(arr.join('-'), "-1.1-1-0");
const reducer = (accumulator, currentValue) => accumulator + currentValue;
assertEquals(arr.reduce(reducer), 2.1);
assertEquals(arr.reduceRight(reducer), 2.1);
assertEquals(arr.slice(0, 1), [,]);
assertEquals(arr.slice(1, 2), [1.1]);
}
var arr1 = [, 1.1, 1, 0];
assertTrue(%HasHoleyElements(arr1));
Object.seal(arr1);
testHoleyDoubleSealedArray2(arr1);
// Verify change from non-extensible to sealed
var arr1 = [, 1.1, 1, 0];
assertTrue(%HasHoleyElements(arr1));
Object.preventExtensions(arr1);
Object.seal(arr1);
testHoleyDoubleSealedArray2(arr1);
// Test regression with Object.defineProperty
var obj = [1.1, , 0];
obj.propertyA = 42;
obj[0] = 1.2;
Object.seal(obj);
assertDoesNotThrow(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
});
});
assertEquals(obj, obj.propertyA);
assertDoesNotThrow(function() {
Object.defineProperty(obj, 'propertyA', {
value: obj,
writable: false,
});
});
obj.propertyA = 42;
assertEquals(obj.propertyA, 42);
assertThrows(function() {
Object.defineProperty(obj, 'abc', {
value: obj,
});
}, TypeError);
// Regression test with simple holey array
var arr = [, 1.1];
Object.seal(arr);
arr[1] = 'b';
assertEquals(arr[1], 'b');
arr[0] = 1;
assertEquals(arr[0], undefined);
// Test regression Array.concat with double
var arr = [1.1, , 0];
Object.seal(arr);
arr = arr.concat(0.5);
assertEquals(arr, [1.1, , 0, 0.5]);
Object.seal(arr);
arr = arr.concat([1.5, 'c']);
assertEquals(arr, [1.1, ,0, 0.5, 1.5, 'c']);
// Regression test with change length
var arr = [1.1, ,0];
Object.seal(arr);
assertEquals(arr.length, 3);
arr.length = 4;
assertEquals(arr.length, 4);
arr[3] = 'c';
assertEquals(arr[2], 0);
assertEquals(arr[3], undefined);
arr.length = 2;
assertEquals(arr.length, 3);
// Change length with holey entries at the end
var arr = [1.1, ,];
Object.seal(arr);
assertEquals(arr.length, 2);
arr.length = 0;
assertEquals(arr.length, 1);
arr.length = 3;
assertEquals(arr.length, 3);
arr.length = 0;
assertEquals(arr.length, 1);
// Spread with array
var arr = [1.1, 0, -1];
Object.seal(arr);
var arrSpread = [...arr];
assertEquals(arrSpread.length, arr.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with array-like
function returnArgs() {
return Object.seal(arguments);
}
var arrLike = returnArgs(1.1, 0, -1);
assertTrue(Object.isSealed(arrLike));
var arrSpread = [...arrLike];
assertEquals(arrSpread.length, arrLike.length);
assertEquals(arrSpread[0], 1.1);
assertEquals(arrSpread[1], 0);
assertEquals(arrSpread[2], -1);
// Spread with holey
function countArgs() {
return arguments.length;
}
var arr = [, 1.1, 0];
Object.seal(arr);
assertEquals(countArgs(...arr), 3);
assertEquals(countArgs(...[...arr]), 3);
assertEquals(countArgs.apply(this, [...arr]), 3);
function checkUndefined() {
return arguments[0] === undefined;
}
assertTrue(checkUndefined(...arr));
assertTrue(checkUndefined(...[...arr]));
assertTrue(checkUndefined.apply(this, [...arr]));
//
// Array.prototype.map
//
(function() {
var a = Object.seal([0.1,1,2,3,4]);
// Simple use.
var result = [1.1,2,3,4,5];
assertArrayEquals(result, a.map(function(n) { return Number(n) + 1; }));
// Use specified object as this object when calling the function.
var o = { delta: 42 }
result = [42.1,43,44,45,46];
assertArrayEquals(result, a.map(function(n) { return this.delta + Number(n); }, o));
// Modify original array.
b = Object.seal([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
b.map(function(n, index, array) {
array[index] = Number(n) + 1; return Number(n) + 1;
}));
assertArrayEquals(b, result);
// Only loop through initial part of array and elements are not
// added.
a = Object.seal([0.1,1,2,3,4]);
result = [1.1,2,3,4,5];
assertArrayEquals(result,
a.map(function(n, index, array) { assertThrows(() => { array.push(n) }); return Number(n) + 1; }));
assertArrayEquals([0.1,1,2,3,4], a);
// Respect holes.
a = new Array(20);
a[1] = 1.1;
Object.seal(a);
a = Object.seal(a).map(function(n) { return 2*Number(n); });
for (var i in a) {
assertEquals(2.2, a[i]);
assertEquals('1', i);
}
// Skip over missing properties.
a = {
"0": 1.1,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2.2, , 4],
Array.prototype.map.call(Object.seal(a), function(n) {
received.push(n);
return n * 2;
}));
assertArrayEquals([1.1, 2], received);
// Modify array prototype
a = [1.1, , 2];
received = [];
assertThrows(() => {
Array.prototype.map.call(Object.seal(a), function(n) {
a.__proto__ = null;
received.push(n);
return n * 2;
});
}, TypeError);
assertArrayEquals([], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
Object.seal([1.1, 2]).map(function() { a.push(this) }, "");
assertTrue(a[0] !== a[1]);
// Do not create a new object otherwise.
a = [];
Object.seal([1.1, 2]).map(function() { a.push(this) }, {});
assertSame(a[0], a[1]);
// In strict mode primitive values should not be coerced to an object.
a = [];
Object.seal([1.1, 1.2]).map(function() { 'use strict'; a.push(this); }, "");
assertEquals("", a[0]);
assertEquals(a[0], a[1]);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment