Revision: 24620
Author: [email protected]
Date: Wed Oct 15 09:11:32 2014 UTC
Log: 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
[email protected], [email protected], [email protected]
Review URL: https://codereview.chromium.org/553413002
Patch from Diego Pino <[email protected]>.
https://code.google.com/p/v8/source/detail?r=24620
Modified:
/branches/bleeding_edge/src/array.js
/branches/bleeding_edge/src/collection.js
/branches/bleeding_edge/src/harmony-array.js
/branches/bleeding_edge/src/macros.py
/branches/bleeding_edge/test/mjsunit/array-iteration.js
/branches/bleeding_edge/test/mjsunit/es6/collections.js
/branches/bleeding_edge/test/mjsunit/harmony/array-find.js
/branches/bleeding_edge/test/mjsunit/harmony/array-findindex.js
=======================================
--- /branches/bleeding_edge/src/array.js Tue Sep 30 15:07:21 2014 UTC
+++ /branches/bleeding_edge/src/array.js Wed Oct 15 09:11:32 2014 UTC
@@ -1133,10 +1133,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
=======================================
--- /branches/bleeding_edge/src/collection.js Tue Aug 19 15:15:41 2014 UTC
+++ /branches/bleeding_edge/src/collection.js Wed Oct 15 09:11:32 2014 UTC
@@ -105,6 +105,12 @@
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 @@
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 @@
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);
}
}
=======================================
--- /branches/bleeding_edge/src/harmony-array.js Tue Aug 19 11:38:38 2014
UTC
+++ /branches/bleeding_edge/src/harmony-array.js Wed Oct 15 09:11:32 2014
UTC
@@ -26,16 +26,18 @@
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 @@
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;
}
}
=======================================
--- /branches/bleeding_edge/src/macros.py Fri Oct 10 14:59:53 2014 UTC
+++ /branches/bleeding_edge/src/macros.py Wed Oct 15 09:11:32 2014 UTC
@@ -167,6 +167,7 @@
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
=======================================
--- /branches/bleeding_edge/test/mjsunit/array-iteration.js Fri Nov 22
13:50:39 2013 UTC
+++ /branches/bleeding_edge/test/mjsunit/array-iteration.js Wed Oct 15
09:11:32 2014 UTC
@@ -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]);
+
})();
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/collections.js Tue Aug 19
15:15:41 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/es6/collections.js Wed Oct 15
09:11:32 2014 UTC
@@ -691,6 +691,33 @@
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 @@
})();
+(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};
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/array-find.js Fri Nov 22
13:50:39 2013 UTC
+++ /branches/bleeding_edge/test/mjsunit/harmony/array-find.js Wed Oct 15
09:11:32 2014 UTC
@@ -237,6 +237,24 @@
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
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/array-findindex.js Fri Nov
22 13:50:39 2013 UTC
+++ /branches/bleeding_edge/test/mjsunit/harmony/array-findindex.js Wed Oct
15 09:11:32 2014 UTC
@@ -237,6 +237,24 @@
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
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.