Array.prototype.{every, filter, find, findIndex, forEach, map, some}: Use fresh primitive wrapper for calls.
When the receiver is a primitive value, it's cast to an Object before entering the loop. Instead, it should be cast to an Object for each function call while in the loop. BUG=v8:3536 LOG=Y R=arv@chromium.org, svenpanne@chromium.org, wingo@igalia.com Review URL: https://codereview.chromium.org/553413002 Patch from Diego Pino <dpino@igalia.com>. git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24620 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
985db1a3c8
commit
e216ab1d40
40
src/array.js
40
src/array.js
@ -1133,10 +1133,11 @@ function ArrayFilter(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [ f ]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
|
||||
receiver = ToObject(receiver);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var result = new $Array();
|
||||
@ -1148,7 +1149,8 @@ function ArrayFilter(f, receiver) {
|
||||
var element = array[i];
|
||||
// Prepare break slots for debugger step in.
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
if (%_CallFunction(receiver, element, i, array, f)) {
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
if (%_CallFunction(new_receiver, element, i, array, f)) {
|
||||
accumulator[accumulator_length++] = element;
|
||||
}
|
||||
}
|
||||
@ -1169,10 +1171,11 @@ function ArrayForEach(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [ f ]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
|
||||
receiver = ToObject(receiver);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
|
||||
@ -1181,7 +1184,8 @@ function ArrayForEach(f, receiver) {
|
||||
var element = array[i];
|
||||
// Prepare break slots for debugger step in.
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
%_CallFunction(receiver, element, i, array, f);
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
%_CallFunction(new_receiver, element, i, array, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1200,10 +1204,11 @@ function ArraySome(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [ f ]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
|
||||
receiver = ToObject(receiver);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
|
||||
@ -1212,7 +1217,8 @@ function ArraySome(f, receiver) {
|
||||
var element = array[i];
|
||||
// Prepare break slots for debugger step in.
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
if (%_CallFunction(receiver, element, i, array, f)) return true;
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
if (%_CallFunction(new_receiver, element, i, array, f)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -1230,10 +1236,11 @@ function ArrayEvery(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [ f ]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
|
||||
receiver = ToObject(receiver);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
|
||||
@ -1242,7 +1249,8 @@ function ArrayEvery(f, receiver) {
|
||||
var element = array[i];
|
||||
// Prepare break slots for debugger step in.
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
if (!%_CallFunction(receiver, element, i, array, f)) return false;
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
if (!%_CallFunction(new_receiver, element, i, array, f)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -1259,10 +1267,11 @@ function ArrayMap(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [ f ]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
|
||||
receiver = ToObject(receiver);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var result = new $Array();
|
||||
@ -1273,7 +1282,8 @@ function ArrayMap(f, receiver) {
|
||||
var element = array[i];
|
||||
// Prepare break slots for debugger step in.
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
accumulator[i] = %_CallFunction(receiver, element, i, array, f);
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
accumulator[i] = %_CallFunction(new_receiver, element, i, array, f);
|
||||
}
|
||||
}
|
||||
%MoveArrayContents(accumulator, result);
|
||||
|
@ -105,6 +105,12 @@ function SetForEach(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [f]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
|
||||
var key;
|
||||
@ -113,7 +119,8 @@ function SetForEach(f, receiver) {
|
||||
while (%SetIteratorNext(iterator, value_array)) {
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
key = value_array[0];
|
||||
%_CallFunction(receiver, key, key, this, f);
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
%_CallFunction(new_receiver, key, key, this, f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,13 +256,20 @@ function MapForEach(f, receiver) {
|
||||
if (!IS_SPEC_FUNCTION(f)) {
|
||||
throw MakeTypeError('called_non_callable', [f]);
|
||||
}
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
||||
receiver = %GetDefaultReceiver(f) || receiver;
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
|
||||
}
|
||||
|
||||
var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
|
||||
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
|
||||
var value_array = [UNDEFINED, UNDEFINED];
|
||||
while (%MapIteratorNext(iterator, value_array)) {
|
||||
if (stepping) %DebugPrepareStepInIfStepping(f);
|
||||
%_CallFunction(receiver, value_array[1], value_array[0], this, f);
|
||||
var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
|
||||
%_CallFunction(new_receiver, value_array[1], value_array[0], this, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,16 +26,18 @@ function ArrayFind(predicate /* thisArg */) { // length == 1
|
||||
thisArg = %_Arguments(1);
|
||||
}
|
||||
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(thisArg)) {
|
||||
thisArg = %GetDefaultReceiver(predicate) || thisArg;
|
||||
} else if (!IS_SPEC_OBJECT(thisArg) && %IsSloppyModeFunction(predicate)) {
|
||||
thisArg = ToObject(thisArg);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(predicate, thisArg);
|
||||
}
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (i in array) {
|
||||
var element = array[i];
|
||||
if (%_CallFunction(thisArg, element, i, array, predicate)) {
|
||||
var newThisArg = needs_wrapper ? ToObject(thisArg) : thisArg;
|
||||
if (%_CallFunction(newThisArg, element, i, array, predicate)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
@ -61,16 +63,18 @@ function ArrayFindIndex(predicate /* thisArg */) { // length == 1
|
||||
thisArg = %_Arguments(1);
|
||||
}
|
||||
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL_OR_UNDEFINED(thisArg)) {
|
||||
thisArg = %GetDefaultReceiver(predicate) || thisArg;
|
||||
} else if (!IS_SPEC_OBJECT(thisArg) && %IsSloppyModeFunction(predicate)) {
|
||||
thisArg = ToObject(thisArg);
|
||||
} else {
|
||||
needs_wrapper = SHOULD_CREATE_WRAPPER(predicate, thisArg);
|
||||
}
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (i in array) {
|
||||
var element = array[i];
|
||||
if (%_CallFunction(thisArg, element, i, array, predicate)) {
|
||||
var newThisArg = needs_wrapper ? ToObject(thisArg) : thisArg;
|
||||
if (%_CallFunction(newThisArg, element, i, array, predicate)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +167,7 @@ macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : NonNumberToNumber
|
||||
macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
|
||||
macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
|
||||
macro HAS_OWN_PROPERTY(obj, index) = (%_CallFunction(obj, index, ObjectHasOwnProperty));
|
||||
macro SHOULD_CREATE_WRAPPER(functionName, receiver) = (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(functionName));
|
||||
|
||||
# Private names.
|
||||
# GET_PRIVATE should only be used if the property is known to exists on obj
|
||||
|
@ -68,6 +68,23 @@
|
||||
assertEquals(3, count);
|
||||
for (var i in a) assertEquals(2, a[i]);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].filter(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].filter(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].filter(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@ -109,6 +126,23 @@
|
||||
a.forEach(function(n) { count++; });
|
||||
assertEquals(1, count);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].forEach(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].forEach(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].forEach(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@ -149,6 +183,23 @@
|
||||
assertTrue(a.every(function(n) { count++; return n == 2; }));
|
||||
assertEquals(2, count);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].every(function() { a.push(this); return true; }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].every(function() { a.push(this); return true; }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].every(function() { 'use strict'; a.push(this); return true; }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
//
|
||||
@ -186,6 +237,23 @@
|
||||
a = a.map(function(n) { return 2*n; });
|
||||
for (var i in a) assertEquals(4, a[i]);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].map(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].map(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].map(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
//
|
||||
@ -224,4 +292,21 @@
|
||||
assertTrue(a.some(function(n) { count++; return n == 2; }));
|
||||
assertEquals(2, count);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].some(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].some(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].some(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
@ -691,6 +691,33 @@ for (var i = 9; i >= 0; i--) {
|
||||
assertEquals(4950, accumulated);
|
||||
})();
|
||||
|
||||
|
||||
(function TestSetForEachReceiverAsObject() {
|
||||
var set = new Set(["1", "2"]);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
var a = [];
|
||||
set.forEach(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
set.forEach(function() { a.push(this); }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
})();
|
||||
|
||||
|
||||
(function TestSetForEachReceiverAsObjectInStrictMode() {
|
||||
var set = new Set(["1", "2"]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
var a = [];
|
||||
set.forEach(function() { 'use strict'; a.push(this); }, "");
|
||||
assertTrue(a[0] === "" && a[0] === a[1]);
|
||||
})();
|
||||
|
||||
|
||||
(function TestMapForEachInvalidTypes() {
|
||||
assertThrows(function() {
|
||||
Map.prototype.map.forEach.call({});
|
||||
@ -998,6 +1025,36 @@ for (var i = 9; i >= 0; i--) {
|
||||
})();
|
||||
|
||||
|
||||
(function TestMapForEachReceiverAsObject() {
|
||||
var map = new Map();
|
||||
map.set("key1", "value1");
|
||||
map.set("key2", "value2");
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
var a = [];
|
||||
map.forEach(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
map.forEach(function() { a.push(this); }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
})();
|
||||
|
||||
|
||||
(function TestMapForEachReceiverAsObjectInStrictMode() {
|
||||
var map = new Map();
|
||||
map.set("key1", "value1");
|
||||
map.set("key2", "value2");
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
var a = [];
|
||||
map.forEach(function() { 'use strict'; a.push(this); }, "");
|
||||
assertTrue(a[0] === "" && a[0] === a[1]);
|
||||
})();
|
||||
|
||||
|
||||
// Allows testing iterator-based constructors easily.
|
||||
var oneAndTwo = new Map();
|
||||
var k0 = {key: 0};
|
||||
|
@ -237,6 +237,24 @@ assertEquals(22, a.find(function(val) { return 22 === val; }), undefined);
|
||||
return this.elementAt(key) === val;
|
||||
}, thisArg);
|
||||
assertEquals("b", found);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].find(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].find(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].find(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
// Test exceptions
|
||||
|
@ -237,6 +237,24 @@ assertEquals(3, a.findIndex(function(val) { return 24 === val; }));
|
||||
return this.elementAt(key) === val;
|
||||
}, thisArg);
|
||||
assertEquals(1, index);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
[1, 2].findIndex(function() { a.push(this) }, "");
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
[1, 2].findIndex(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
[1, 2].findIndex(function() { 'use strict'; a.push(this); }, "");
|
||||
assertEquals("", a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
})();
|
||||
|
||||
// Test exceptions
|
||||
|
Loading…
Reference in New Issue
Block a user