ToString of a Proxied function should not throw

Without --harmony-function-tostring, anything other than a JSFunction
or JSBoundFunction throw when Function.prototype.toString is called on
them. But with the toString revision, anything callable allows toString
(and for non-Functions returns the good old "function () { [native code] }"
string).

Bug: v8:7484
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I3540e213a40992151761b59666fe36e0510da908
Reviewed-on: https://chromium-review.googlesource.com/932825
Commit-Queue: Adam Klein <adamk@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51489}
diff --git a/src/builtins/builtins-function.cc b/src/builtins/builtins-function.cc
index 771c724..1f58c98 100644
--- a/src/builtins/builtins-function.cc
+++ b/src/builtins/builtins-function.cc
@@ -305,9 +305,16 @@
   Handle<Object> receiver = args.receiver();
   if (receiver->IsJSBoundFunction()) {
     return *JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(receiver));
-  } else if (receiver->IsJSFunction()) {
+  }
+  if (receiver->IsJSFunction()) {
     return *JSFunction::ToString(Handle<JSFunction>::cast(receiver));
   }
+  // With the revised toString behavior, all callable objects are valid
+  // receivers for this method.
+  if (FLAG_harmony_function_tostring && receiver->IsJSReceiver() &&
+      JSReceiver::cast(*receiver)->map()->is_callable()) {
+    return isolate->heap()->function_native_code_string();
+  }
   THROW_NEW_ERROR_RETURN_FAILURE(
       isolate, NewTypeError(MessageTemplate::kNotGeneric,
                             isolate->factory()->NewStringFromAsciiChecked(
diff --git a/src/heap-symbols.h b/src/heap-symbols.h
index 6efe9da..ae06892 100644
--- a/src/heap-symbols.h
+++ b/src/heap-symbols.h
@@ -79,6 +79,7 @@
   V(Float64Array_string, "Float64Array")                           \
   V(fraction_string, "fraction")                                   \
   V(Function_string, "Function")                                   \
+  V(function_native_code_string, "function () { [native code] }")  \
   V(function_string, "function")                                   \
   V(function_to_string, "[object Function]")                       \
   V(Generator_string, "Generator")                                 \
diff --git a/src/objects.cc b/src/objects.cc
index 085776a..e4bb52d 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -13212,9 +13212,6 @@
 
 namespace {
 
-char const kNativeCodeSource[] = "function () { [native code] }";
-
-
 Handle<String> NativeCodeFunctionSourceString(
     Handle<SharedFunctionInfo> shared_info) {
   Isolate* const isolate = shared_info->GetIsolate();
@@ -13231,7 +13228,7 @@
 // static
 Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
   Isolate* const isolate = function->GetIsolate();
-  return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource);
+  return isolate->factory()->function_native_code_string();
 }
 
 
diff --git a/test/mjsunit/es6/proxies.js b/test/mjsunit/es6/proxies.js
index 75a80a1..f67f9df 100644
--- a/test/mjsunit/es6/proxies.js
+++ b/test/mjsunit/es6/proxies.js
@@ -1287,8 +1287,7 @@
 
 // ---------------------------------------------------------------------------
 // String conversion (Object.prototype.toString,
-//                    Object.prototype.toLocaleString,
-//                    Function.prototype.toString)
+//                    Object.prototype.toLocaleString)
 
 var key
 
@@ -1306,7 +1305,6 @@
   assertEquals(Symbol.toStringTag, key)
   assertEquals("my_proxy", Object.prototype.toLocaleString.call(f))
   assertEquals("toString", key)
-  assertThrows(function(){ Function.prototype.toString.call(f) })
 
   var o = Object.create(p)
   key = ""
diff --git a/test/mjsunit/es6/proxy-function-tostring.js b/test/mjsunit/es6/proxy-function-tostring.js
new file mode 100644
index 0000000..d859822
--- /dev/null
+++ b/test/mjsunit/es6/proxy-function-tostring.js
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+//
+// Flags: --noharmony-function-tostring
+
+assertThrows(() => new Proxy(function() {}, {}).toString(), TypeError);
diff --git a/test/mjsunit/harmony/function-tostring.js b/test/mjsunit/harmony/function-tostring.js
index 949ac22..8fccf0d 100644
--- a/test/mjsunit/harmony/function-tostring.js
+++ b/test/mjsunit/harmony/function-tostring.js
@@ -122,3 +122,25 @@
 testDynamicFunction("a,/*A*/b", "return a");
 testDynamicFunction("/*A*/a,b", "return a");
 testDynamicFunction("a,b", "return a/*A*/");
+
+// Proxies of functions should not throw, but return a NativeFunction.
+assertEquals("function () { [native code] }",
+             new Proxy(function () { hidden }, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(() => { hidden }, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(class {}, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(function() { hidden }.bind({}), {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(function*() { hidden }, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(async function() { hidden }, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy(async function*() { hidden }, {}).toString());
+assertEquals("function () { [native code] }",
+             new Proxy({ method() { hidden } }.method, {}).toString());
+
+// Non-callable proxies still throw.
+assertThrows(() => Function.prototype.toString.call(new Proxy({}, {})),
+             TypeError);