瀏覽代碼

Set the implicit call flag on partial length change

In the presence of non-configurable, numeric properties, setting the
length of an ES5Array can only decrease the length to the maximum of
the highest numerically indexed length property less than the length
and the new length. We were overeagerly propagating the newer length
with the copy prop machinery, and not checking that we actually were
able to assign this length value correctly. This changes the helpers
for setting the length property on ES5Arrays to set the implicitcall
flag when the new length isn't fully applied.

Relevant test case included.
Derek Morris 9 年之前
父節點
當前提交
509f944a26

+ 5 - 1
lib/Runtime/Library/ES5Array.cpp

@@ -221,7 +221,11 @@ namespace Js
             }
 
             uint32 newLen = ToLengthValue(value, scriptContext);
-            GetTypeHandler()->SetLength(this, newLen, propertyOperationFlags);
+            uint32 assignedLen = GetTypeHandler()->SetLength(this, newLen, propertyOperationFlags);
+            if (newLen != assignedLen)
+            {
+                scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
+            }
             *result = true;
             return true;
         }

+ 2 - 1
lib/Runtime/Types/ES5ArrayTypeHandler.cpp

@@ -950,7 +950,7 @@ namespace Js
     }
 
     template <class T>
-    void ES5ArrayTypeHandlerBase<T>::SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags)
+    uint32 ES5ArrayTypeHandlerBase<T>::SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags)
     {
         Assert(IsLengthWritable()); // Should have already checked
 
@@ -966,6 +966,7 @@ namespace Js
         // Strict mode TODO: In strict mode we may need to throw if we cannot delete to
         // requested newLen (ES5 15.4.5.1 3.l.III.4).
         //
+        return newLen;
     }
 
     template <class T>

+ 1 - 1
lib/Runtime/Types/ES5ArrayTypeHandler.h

@@ -187,7 +187,7 @@ namespace Js
         virtual BOOL SetAttributes(DynamicObject* instance, PropertyId propertyId, PropertyAttributes attributes) override;
 
         virtual bool IsLengthWritable() const override;
-        virtual void SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags) override;
+        virtual uint32 SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags) override;
         virtual BOOL IsObjectArrayFrozen(ES5Array* arr) override;
         virtual BOOL IsItemEnumerable(ES5Array* arr, uint32 index) override;
         virtual BOOL IsValidDescriptorToken(void * descriptorValidationToken) const override;

+ 1 - 1
lib/Runtime/Types/TypeHandler.h

@@ -485,7 +485,7 @@ namespace Js
 
         // ES5Array type handler specific methods. Only implemented by ES5ArrayTypeHandlers.
         virtual bool IsLengthWritable() const { Assert(false); return false; }
-        virtual void SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags) { Assert(false); }
+        virtual uint32 SetLength(ES5Array* arr, uint32 newLen, PropertyOperationFlags propertyOperationFlags) { Assert(false); return 0; }
         virtual BOOL IsObjectArrayFrozen(ES5Array* arr) { Assert(false); return FALSE; }
         virtual BOOL IsItemEnumerable(ES5Array* arr, uint32 index) { Assert(false); return FALSE; }
         virtual BOOL IsValidDescriptorToken(void * descriptorValidationToken) const { Assert(false); return FALSE; }

+ 30 - 0
test/es5/es5_defineProperty_arrayLength.js

@@ -0,0 +1,30 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function check(val, equals, msg) {
+    if(val !== equals) {
+        throw new Error(msg);
+    }
+}
+
+function test () {
+    var arr = [1, 2, 3, 4];
+    Object.defineProperty(arr, 1, {configurable: false});
+    for(var i = 0; i < 80; i++) {
+        if(!arr.length) {
+            arr[0] = -1;
+        }
+        else {
+            arr.length = 0;
+            check(arr.length, 2, "cannot delete a non-configurable property");
+        }
+    }
+}
+
+for(var j = 0; j < 80; j++) {
+    test();
+}
+
+print("PASS");

+ 5 - 0
test/es5/rlexe.xml

@@ -349,4 +349,9 @@
       <baseline>es5array_enum_edit.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>es5_defineProperty_arrayLength.js</files>
+    </default>
+  </test>
 </regress-exe>