Fix ToObject and Object.isSealed in four Array builtins.
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 23 Apr 2014 12:48:32 +0000 (12:48 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 23 Apr 2014 12:48:32 +0000 (12:48 +0000)
R=mvstanton@chromium.org
TEST=mjsunit/regress/regress-builtinbust-6

Review URL: https://codereview.chromium.org/240223006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20909 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/array.js
test/mjsunit/regress/regress-builtinbust-6.js [new file with mode: 0644]

index a233c63..104fe3f 100644 (file)
@@ -415,24 +415,20 @@ function ObservedArrayPop(n) {
 function ArrayPop() {
   CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
 
-  var n = TO_UINT32(this.length);
+  var array = TO_OBJECT_INLINE(this);
+  var n = TO_UINT32(array.length);
   if (n == 0) {
-    this.length = n;
+    array.length = n;
     return;
   }
 
-  if (ObjectIsSealed(this)) {
-    throw MakeTypeError("array_functions_change_sealed",
-                        ["Array.prototype.pop"]);
-  }
-
-  if (%IsObserved(this))
-    return ObservedArrayPop.call(this, n);
+  if (%IsObserved(array))
+    return ObservedArrayPop.call(array, n);
 
   n--;
-  var value = this[n];
-  Delete(this, ToName(n), true);
-  this.length = n;
+  var value = array[n];
+  Delete(array, ToName(n), true);
+  array.length = n;
   return value;
 }
 
@@ -461,24 +457,21 @@ function ObservedArrayPush() {
 function ArrayPush() {
   CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
 
-  var n = TO_UINT32(this.length);
-  var m = %_ArgumentsLength();
-
   if (%IsObserved(this))
     return ObservedArrayPush.apply(this, arguments);
 
+  var array = TO_OBJECT_INLINE(this);
+  var n = TO_UINT32(array.length);
+  var m = %_ArgumentsLength();
+
   for (var i = 0; i < m; i++) {
     // Use SetProperty rather than a direct keyed store to ensure that the store
     // site doesn't become poisened with an elements transition KeyedStoreIC.
-    //
-    // TODO(danno): Using %SetProperty is a temporary workaround. The spec says
-    // that ToObject needs to be called for primitive values (and
-    // Runtime_SetProperty seem to ignore them).
-    %SetProperty(this, i+n, %_Arguments(i), 0, kStrictMode);
+    %SetProperty(array, i+n, %_Arguments(i), 0, kStrictMode);
   }
 
   var new_length = n + m;
-  this.length = new_length;
+  array.length = new_length;
   return new_length;
 }
 
@@ -596,30 +589,31 @@ function ObservedArrayShift(len) {
 function ArrayShift() {
   CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
 
-  var len = TO_UINT32(this.length);
+  var array = TO_OBJECT_INLINE(this);
+  var len = TO_UINT32(array.length);
 
   if (len === 0) {
-    this.length = 0;
+    array.length = 0;
     return;
   }
 
-  if (ObjectIsSealed(this)) {
+  if (ObjectIsSealed(array)) {
     throw MakeTypeError("array_functions_change_sealed",
                         ["Array.prototype.shift"]);
   }
 
-  if (%IsObserved(this))
-    return ObservedArrayShift.call(this, len);
+  if (%IsObserved(array))
+    return ObservedArrayShift.call(array, len);
 
-  var first = this[0];
+  var first = array[0];
 
-  if (IS_ARRAY(this)) {
-    SmartMove(this, 0, 1, len, 0);
+  if (IS_ARRAY(array)) {
+    SmartMove(array, 0, 1, len, 0);
   } else {
-    SimpleMove(this, 0, 1, len, 0);
+    SimpleMove(array, 0, 1, len, 0);
   }
 
-  this.length = len - 1;
+  array.length = len - 1;
 
   return first;
 }
@@ -647,25 +641,26 @@ function ObservedArrayUnshift() {
 function ArrayUnshift(arg1) {  // length == 1
   CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
 
-  var len = TO_UINT32(this.length);
-  var num_arguments = %_ArgumentsLength();
-  var is_sealed = ObjectIsSealed(this);
-
   if (%IsObserved(this))
     return ObservedArrayUnshift.apply(this, arguments);
 
-  if (IS_ARRAY(this) && !is_sealed) {
-    SmartMove(this, 0, 0, len, num_arguments);
+  var array = TO_OBJECT_INLINE(this);
+  var len = TO_UINT32(array.length);
+  var num_arguments = %_ArgumentsLength();
+  var is_sealed = ObjectIsSealed(array);
+
+  if (IS_ARRAY(array) && !is_sealed) {
+    SmartMove(array, 0, 0, len, num_arguments);
   } else {
-    SimpleMove(this, 0, 0, len, num_arguments);
+    SimpleMove(array, 0, 0, len, num_arguments);
   }
 
   for (var i = 0; i < num_arguments; i++) {
-    this[i] = %_Arguments(i);
+    array[i] = %_Arguments(i);
   }
 
   var new_length = len + num_arguments;
-  this.length = new_length;
+  array.length = new_length;
   return new_length;
 }
 
diff --git a/test/mjsunit/regress/regress-builtinbust-6.js b/test/mjsunit/regress/regress-builtinbust-6.js
new file mode 100644 (file)
index 0000000..771f80a
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test that Array builtins can be called on primitive values.
+var values = [ 23, 4.2, true, false, 0/0 ];
+for (var i = 0; i < values.length; ++i) {
+  var v = values[i];
+  Array.prototype.pop.call(v);
+  Array.prototype.push.call(v);
+  Array.prototype.shift.call(v);
+  Array.prototype.unshift.call(v);
+}
+
+// Test that ToObject on primitive values is only called once.
+var length_receiver, element_receiver;
+function length() { length_receiver = this; return 1; }
+function element() { element_receiver = this; return "x"; }
+Object.defineProperty(Number.prototype, "length", { get:length, set:length });
+Object.defineProperty(Number.prototype, "0", { get:element, set:element });
+Object.defineProperty(Number.prototype, "1", { get:element, set:element });
+
+assertDoesNotThrow("Array.prototype.pop.call(23)");
+assertEquals(new Number(23), length_receiver);
+assertSame(length_receiver, element_receiver);
+
+assertDoesNotThrow("Array.prototype.push.call(42, 'y')");
+assertEquals(new Number(42), length_receiver);
+assertSame(length_receiver, element_receiver);
+
+assertDoesNotThrow("Array.prototype.shift.call(65)");
+assertEquals(new Number(65), length_receiver);
+assertSame(length_receiver, element_receiver);
+
+assertDoesNotThrow("Array.prototype.unshift.call(99, 'z')");
+assertEquals(new Number(99), length_receiver);
+assertSame(length_receiver, element_receiver);