ソースを参照

Set isNotPathTypeHandlerOrHasUserDefinedCtor flag for Ctors as early as possible
Updated tests for Array.map and .filter to cover poisoned ctor

Irina Yatsenko 8 年 前
コミット
29a055c4eb

+ 5 - 4
lib/Runtime/Types/PathTypeHandler.cpp

@@ -2012,6 +2012,11 @@ namespace Js
             }
 #endif
 
+            if (key.GetPropertyId() == PropertyIds::constructor)
+            {
+                nextPath->isNotPathTypeHandlerOrHasUserDefinedCtor = true;
+            }
+
 #ifdef PROFILE_TYPES
             scriptContext->maxPathLength = max(GetPathLength() + 1, scriptContext->maxPathLength);
 #endif
@@ -2241,10 +2246,6 @@ namespace Js
 
         Assert(instance->GetTypeHandler()->IsPathTypeHandler());
         PathTypeHandlerBase* newTypeHandler = (PathTypeHandlerBase*)newType->GetTypeHandler();
-        if (propertyId == PropertyIds::constructor)
-        {
-            newTypeHandler->isNotPathTypeHandlerOrHasUserDefinedCtor = true;
-        }
 
         Assert(newType->GetIsShared() == newTypeHandler->GetIsShared());
 

+ 0 - 38
test/es5/array_filter.baseline

@@ -1,38 +0,0 @@
-value:1 index:0 Object:1,2,3,4,5
-value:2 index:1 Object:1,2,3,4,5
-value:3 index:2 Object:1,2,3,4,5
-value:4 index:3 Object:1,2,3,4,5
-value:5 index:4 Object:1,2,3,4,5
-1,2,3,4,5
-value:10 index:0 Object:10,20,30,40,50
-value:20 index:1 Object:10,20,30,40,50
-value:30 index:2 Object:10,20,30,40,50
-value:40 index:3 Object:10,20,30,40,50
-value:50 index:4 Object:10,20,30,40,50
-
-value:10 index:0 Object:10,20,30,40,50
-value:20 index:1 Object:10,20,30,40,50
-value:30 index:2 Object:10,20,30,40,50
-value:40 index:3 Object:10,20,30,40,50
-value:50 index:4 Object:10,20,30,40,50
-10,30,40,50
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-abc,def,xyz
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-abc,xyz
-value:10 index:0 Object:10,20,30,40,50,,20,,10
-value:20 index:1 Object:10,20,30,40,50,,20,,10
-value:30 index:2 Object:10,20,30,40,50,,20,,10
-value:40 index:3 Object:10,20,30,40,50,,20,,10
-value:50 index:4 Object:10,20,30,40,50,,20,,10
-value:20 index:6 Object:10,20,30,40,50,,20,,10
-value:10 index:8 Object:10,20,30,40,50,,20,,10
-10,20,30,40,50,20,10

+ 93 - 44
test/es5/array_filter.js

@@ -3,51 +3,100 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-function returnTrue(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return true;
+if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
+    this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
 }
 
-function returnFalse(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return false;
-}
-
-function returnRandom(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return y!=1;
-}
-
-Array.prototype[6] = 20;
-
-var x = [1,2,3,4,5];
-var y = x.filter(returnTrue,this);
-WScript.Echo(y);
-
-x = [10,20,30,40,50];
-y = x.filter(returnFalse, this);
-WScript.Echo(y);
-
-x = [10,20,30,40,50];
-y = x.filter(returnRandom, this);
-WScript.Echo(y);
-
-x = {0: "abc", 1: "def", 2: "xyz"}
-x.length = 3;
-
-y = Array.prototype.filter.call(x, returnTrue,this);
-WScript.Echo(y);
-
-y = Array.prototype.filter.call(x, returnFalse,this);
-WScript.Echo(y);
+var tests = [
+    {
+        name: "22.1.3.7: Array.prototype.filter basic case",
+        body: function () {
+            const a = [2, 1, 0];
+            let predicate = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                return value >= index;
+            };
+            const b = a.filter(predicate);
+            assert.areEqual("2,1", b.join(","), "filtered array");
+        }
+    },
+    {
+        name: "22.1.3.7: Array.prototype.filter should skip missing items",
+        body: function () {
+            const a = [1, 2, 3];
+            delete a[1];
+            let callCount = 0;
+            let predicate = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                callCount += 1;
+                return true;
+            };
+            let b = a.filter(predicate);
+            assert.areEqual(2, callCount, "visited two items only");
+            assert.areEqual("1,3", b.join(","), "filtered array");
+        }
+    },
+    {
+        name: "22.1.3.7: mutating array after Array.prototype.filter has started",
+        body: function () {
+            let a = [1, 2, 3];
+            let callCount = 0;
+            let predicate = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                callCount += 1;
 
-y = Array.prototype.filter.call(x, returnRandom, this);
-WScript.Echo(y);
+                if (index === 0) {
+                    delete a[1]; // should be skipped
+                    a[2] = 4; // new value should be used
+                    a[4] = 5; // added items shouldn't be visited
+                }
+                return true;
+            };
+            let b = a.filter(predicate);
+            assert.areEqual(2, callCount, "visited two items only");
+            assert.areEqual("1,4", b.join(","), "filtered array");
+            assert.areEqual("1,,4,,5", a.join(","), "mutated array");
+        }
+    },
+    {
+        name: "22.1.3.7: Array.prototype.filter should call ArraySpeciesCreate which relies on 'constructor' property",
+        body: function () {
+            const a = [1, 2, 3];
+            Object.defineProperty(a, 'constructor', {
+                get: function () {
+                    throw new Error("13");
+                }
+            });
+            assert.throws(function () { a.filter(function () { }); }, Error, "Should throw from constructor", "13");
+        }
+    },
+    {
+        name: "22.1.3.7: Array.prototype.filter might provide 'this' argument to the callback",
+        body: function () {
+            const a = [5, 6, 7];
+            let that = { calls: 0 };
+            let predicate = function (value, index, obj) {
+                this.calls++;
+                return false;
+            };
+            const b = a.filter(predicate, that);
+            assert.areEqual(3, that.calls, "context's 'calls' property");
+            assert.areEqual("", b.join(","), "const 'false' filter should produce empty result");
+        }
+    },
+    {
+        name: "22.1.3.7: Array.prototype.filter is generic and can be applied to other objects",
+        body: function () {
+            let a = { 0: "a", 1: "bc", 2: "de" }
+            a.length = 3;
+            let predicate = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                return value.length > index;
+            };
+            const b = Array.prototype.filter.call(a, predicate);
+            assert.areEqual("a,bc", b.join(","), "filtered object");
+        }
+    }
+];
 
-x = [10,20,30,40,50];
-x[8] = 10;
-y = x.filter(returnTrue, this);
-WScript.Echo(y);
+testRunner.runTests(tests, { verbose: false /*so no need to provide baseline*/ });

+ 0 - 38
test/es5/array_map.baseline

@@ -1,38 +0,0 @@
-value:1 index:0 Object:1,2,3,4,5
-value:2 index:1 Object:1,2,3,4,5
-value:3 index:2 Object:1,2,3,4,5
-value:4 index:3 Object:1,2,3,4,5
-value:5 index:4 Object:1,2,3,4,5
-1,4,9,16,25
-value:10 index:0 Object:10,20,30,40,50
-value:20 index:1 Object:10,20,30,40,50
-value:30 index:2 Object:10,20,30,40,50
-value:40 index:3 Object:10,20,30,40,50
-value:50 index:4 Object:10,20,30,40,50
-0,1,4,9,16
-value:10 index:0 Object:10,20,30,40,50
-value:20 index:1 Object:10,20,30,40,50
-value:30 index:2 Object:10,20,30,40,50
-value:40 index:3 Object:10,20,30,40,50
-value:50 index:4 Object:10,20,30,40,50
-0,20,60,120,200
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-NaN,NaN,NaN
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-0,1,4
-value:abc index:0 Object:[object Object]
-value:def index:1 Object:[object Object]
-value:xyz index:2 Object:[object Object]
-NaN,NaN,NaN
-value:10 index:0 Object:10,20,30,40,50,,20,,10
-value:20 index:1 Object:10,20,30,40,50,,20,,10
-value:30 index:2 Object:10,20,30,40,50,,20,,10
-value:40 index:3 Object:10,20,30,40,50,,20,,10
-value:50 index:4 Object:10,20,30,40,50,,20,,10
-value:20 index:6 Object:10,20,30,40,50,,20,,10
-value:10 index:8 Object:10,20,30,40,50,,20,,10
-100,400,900,1600,2500,,400,,100

+ 92 - 44
test/es5/array_map.js

@@ -3,51 +3,99 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-function returnValueSquare(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return x*x;
+if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
+    this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
 }
 
-function returnIndexSquare(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return y*y;
-}
-
-function returnRandom(x,y,z)
-{
-    WScript.Echo("value:"+ x + " index:" + y + " Object:" + z);
-    return x*y;
-}
-
-Array.prototype[6] = 20;
-
-var x = [1,2,3,4,5];
-var y = x.map(returnValueSquare,this);
-WScript.Echo(y);
-
-x = [10,20,30,40,50];
-y = x.map(returnIndexSquare, this);
-WScript.Echo(y);
-
-x = [10,20,30,40,50];
-y = x.map(returnRandom, this);
-WScript.Echo(y);
-
-x = {0: "abc", 1: "def", 2: "xyz"}
-x.length = 3;
-
-y = Array.prototype.map.call(x, returnValueSquare,this);
-WScript.Echo(y);
-
-y = Array.prototype.map.call(x, returnIndexSquare,this);
-WScript.Echo(y);
+var tests = [
+    {
+        name: "22.1.3.16: Array.prototype.map basic case",
+        body: function () {
+            const a = [5, 6, 7];
+            let transform = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                return value * index;
+            };
+            const b = a.map(transform);
+            assert.areEqual("0,6,14", b.join(","), "transformed array");
+        }
+    },
+    {
+        name: "22.1.3.16: Array.prototype.map should skip missing items",
+        body: function () {
+            const a = [1, 2, 3];
+            delete a[1];
+            let callCount = 0;
+            let transform = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                callCount += 1;
+                return value * 2;
+            };
+            let b = a.map(transform);
+            assert.areEqual(2, callCount, "visited two items only");
+            assert.areEqual("2,,6", b.join(","), "transformed array");
+        }
+    },
+    {
+        name: "22.1.3.16: mutating array after Array.prototype.map has started",
+        body: function () {
+            let a = [1, 2, 3];
+            let callCount = 0;
+            let transform = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                callCount += 1;
 
-y = Array.prototype.map.call(x, returnRandom, this);
-WScript.Echo(y);
+                if (index === 0) {
+                    delete a[1]; // should be skipped
+                    a[2] = 4; // new value should be used
+                    a[4] = 5; // added items shouldn't be visited
+                }
+                return value * index;
+            };
+            let b = a.map(transform);
+            assert.areEqual(2, callCount, "visited two items only");
+            assert.areEqual("0,,8", b.join(","), "transformed array");
+            assert.areEqual("1,,4,,5", a.join(","), "mutated array");
+        }
+    },
+    {
+        name: "22.1.3.16: Array.prototype.map should call ArraySpeciesCreate which relies on 'constructor' property",
+        body: function () {
+            const a = [1, 2, 3];
+            Object.defineProperty(a, 'constructor', {
+                get: function () {
+                    throw new Error("13");
+                }
+            });
+            assert.throws(function () { a.map(function () { }); }, Error, "Should throw from constructor", "13");
+        }
+    },
+    {
+        name: "22.1.3.16: Array.prototype.map might provide 'this' argument to the callback",
+        body: function () {
+            const a = [5, 6, 7];
+            let that = {calls: 0};
+            let transform = function (value, index, obj) {
+                this.calls++;
+                return 0;
+            };
+            a.map(transform, that);
+            assert.areEqual(3, that.calls, "context's 'calls' property");
+        }
+    },
+    {
+        name: "22.1.3.16: Array.prototype.map is generic and can be applied to other objects",
+        body: function () {
+            let a = { 0: "a", 1: "bc", 2: "" }
+            a.length = 3;
+            let transform = function (value, index, obj) {
+                assert.areEqual(obj[index], value);
+                return value + "!";
+            };
+            const b = Array.prototype.map.call(a, transform);
+            assert.areEqual("a!,bc!,!", b.join(","), "transformed object");
+        }
+    }
+];
 
-x = [10,20,30,40,50];
-x[8] = 10;
-y = x.map(returnValueSquare, this);
-WScript.Echo(y);
+testRunner.runTests(tests, { verbose: false /*so no need to provide baseline*/ });

+ 0 - 2
test/es5/rlexe.xml

@@ -34,13 +34,11 @@
   <test>
     <default>
       <files>array_map.js</files>
-      <baseline>array_map.baseline</baseline>
     </default>
   </test>
   <test>
     <default>
       <files>array_filter.js</files>
-      <baseline>array_filter.baseline</baseline>
     </default>
   </test>
   <test>