Do not implicitly convert non-object receivers for strict mode functions.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 5 Apr 2013 11:57:02 +0000 (11:57 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 5 Apr 2013 11:57:02 +0000 (11:57 +0000)
This was still the case for Array.prototype.* builtin functions.

R=rossberg@chromium.org
BUG=v8:2273

Review URL: https://chromiumcodereview.appspot.com/13473009

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

src/array.js
src/runtime.cc
src/runtime.h
test/mjsunit/regress/regress-2273.js [new file with mode: 0644]

index 936d008..c564f4c 100644 (file)
@@ -1017,7 +1017,7 @@ function ArrayFilter(f, receiver) {
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
     receiver = %GetDefaultReceiver(f) || receiver;
-  } else if (!IS_SPEC_OBJECT(receiver)) {
+  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
     receiver = ToObject(receiver);
   }
 
@@ -1068,9 +1068,10 @@ function ArrayForEach(f, receiver) {
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
     receiver = %GetDefaultReceiver(f) || receiver;
-  } else if (!IS_SPEC_OBJECT(receiver)) {
+  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
     receiver = ToObject(receiver);
   }
+
   if (%DebugCallbackSupportsStepping(f)) {
     for (var i = 0; i < length; i++) {
       if (i in array) {
@@ -1111,7 +1112,7 @@ function ArraySome(f, receiver) {
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
     receiver = %GetDefaultReceiver(f) || receiver;
-  } else if (!IS_SPEC_OBJECT(receiver)) {
+  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
     receiver = ToObject(receiver);
   }
 
@@ -1154,7 +1155,7 @@ function ArrayEvery(f, receiver) {
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
     receiver = %GetDefaultReceiver(f) || receiver;
-  } else if (!IS_SPEC_OBJECT(receiver)) {
+  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
     receiver = ToObject(receiver);
   }
 
@@ -1196,7 +1197,7 @@ function ArrayMap(f, receiver) {
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
     receiver = %GetDefaultReceiver(f) || receiver;
-  } else if (!IS_SPEC_OBJECT(receiver)) {
+  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
     receiver = ToObject(receiver);
   }
 
index 66badb8..ac54470 100644 (file)
@@ -1948,6 +1948,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsClassicModeFunction) {
+  NoHandleAllocation ha(isolate);
+  ASSERT(args.length() == 1);
+  CONVERT_ARG_CHECKED(JSReceiver, callable, 0);
+  if (!callable->IsJSFunction()) {
+    HandleScope scope(isolate);
+    bool threw = false;
+    Handle<Object> delegate =
+        Execution::TryGetFunctionDelegate(Handle<JSReceiver>(callable), &threw);
+    if (threw) return Failure::Exception();
+    callable = JSFunction::cast(*delegate);
+  }
+  JSFunction* function = JSFunction::cast(callable);
+  SharedFunctionInfo* shared = function->shared();
+  return isolate->heap()->ToBoolean(shared->is_classic_mode());
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultReceiver) {
   NoHandleAllocation ha(isolate);
   ASSERT(args.length() == 1);
index b16acd1..487bd80 100644 (file)
@@ -64,6 +64,7 @@ namespace internal {
   F(ToFastProperties, 1, 1) \
   F(FinishArrayPrototypeSetup, 1, 1) \
   F(SpecialArrayFunctions, 1, 1) \
+  F(IsClassicModeFunction, 1, 1) \
   F(GetDefaultReceiver, 1, 1) \
   \
   F(GetPrototype, 1, 1) \
diff --git a/test/mjsunit/regress/regress-2273.js b/test/mjsunit/regress/regress-2273.js
new file mode 100644 (file)
index 0000000..7868b8d
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var CheckStringReceiver = function() {
+  "use strict";
+  // Receivers of strict functions are not coerced.
+  assertEquals("string", typeof this);
+};
+
+var CheckNumberReceiver = function() {
+  "use strict";
+  // Receivers of strict functions are not coerced.
+  assertEquals("number", typeof this);
+};
+
+var CheckUndefinedReceiver = function() {
+  "use strict";
+  // Receivers of strict functions are not coerced.
+  assertEquals("undefined", String(this));
+};
+
+var CheckNullReceiver = function() {
+  "use strict";
+  // Receivers of strict functions are not coerced.
+  assertEquals("null", String(this));
+};
+
+var CheckCoersion = function() {
+  // Receivers of non-strict functions are coerced to objects.
+  assertEquals("object", typeof this);
+};
+
+
+function strict_mode() {
+  "use strict";
+  CheckStringReceiver.call("foo");
+  CheckNumberReceiver.call(42);
+  CheckUndefinedReceiver.call(undefined);
+  CheckNullReceiver.call(null);
+  [1].forEach(CheckStringReceiver, "foo");
+  [2].every(CheckStringReceiver, "foo");
+  [3].filter(CheckStringReceiver, "foo");
+  [4].some(CheckNumberReceiver, 42);
+  [5].map(CheckNumberReceiver, 42);
+
+  CheckCoersion.call("foo");
+  CheckCoersion.call(42);
+  CheckCoersion.call(undefined);
+  CheckCoersion.call(null);
+  [1].forEach(CheckCoersion, "foo");
+  [2].every(CheckCoersion, "foo");
+  [3].filter(CheckCoersion, "foo");
+  [4].some(CheckCoersion, 42);
+  [5].map(CheckCoersion, 42);
+};
+strict_mode();
+
+function classic_mode() {
+  CheckStringReceiver.call("foo");
+  CheckNumberReceiver.call(42);
+  CheckUndefinedReceiver.call(undefined);
+  CheckNullReceiver.call(null);
+  [1].forEach(CheckStringReceiver, "foo");
+  [2].every(CheckStringReceiver, "foo");
+  [3].filter(CheckStringReceiver, "foo");
+  [4].some(CheckNumberReceiver, 42);
+  [5].map(CheckNumberReceiver, 42);
+
+  CheckCoersion.call("foo");
+  CheckCoersion.call(42);
+  CheckCoersion.call(undefined);
+  CheckCoersion.call(null);
+  [1].forEach(CheckCoersion, "foo");
+  [2].every(CheckCoersion, "foo");
+  [3].filter(CheckCoersion, "foo");
+  [4].some(CheckCoersion, 42);
+  [5].map(CheckCoersion, 42);
+};
+classic_mode();