Title: [278462] trunk
Revision
278462
Author
[email protected]
Date
2021-06-04 08:58:13 -0700 (Fri, 04 Jun 2021)

Log Message

Optimize Function.prototype.toString
https://bugs.webkit.org/show_bug.cgi?id=226418
<rdar://77861846>

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/function-to-string.js: Added.
(f):
(C):
(C.prototype.method1):
(C.prototype.method2):
(test):
(test2):

Source/_javascript_Core:

Add caching to Function.prototype.toString. This is used heavily in Speedometer2, and repeatedly recomputing a
string which is a constant is costly. We cache the results of toString in all cases except for bound functions.
To make this work for bound functions, we'd need to add a new field they can use for this cache. For other
functions, we cache it on the executable (either NativeExecutable or FunctionExecutable). The reason we can't
do this on the executable for bound functions is that all bound functions share the same executable, but
individual bound functions can have different names. The reason it's valid to cache the results in general is that a
function's name field can't be changed from JS code -- it's non-writable.

This patch also makes Function.prototype.toString an intrinsic in the DFG/FTL. We emit code on the fast path
which reads the cached value if it's present. If not, we call into the slow path, which will compute
the cached value for non bound functions, or compute the result for bound functions.

I added a new microbenchmark that speeds up by >35x:

function-to-string     2197.5952+-30.7118    ^     59.9861+-2.5550        ^ definitely 36.6350x faster

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::getExecutable):
(JSC::DFG::SpeculativeJIT::compileFunctionToString):
(JSC::DFG::SpeculativeJIT::compileGetExecutable):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::getExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileGetExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileFunctionToString):
* runtime/FunctionExecutable.cpp:
(JSC::FunctionExecutable::visitChildrenImpl):
(JSC::FunctionExecutable::toStringSlow):
* runtime/FunctionExecutable.h:
* runtime/FunctionExecutableInlines.h:
(JSC::FunctionExecutable::toString):
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::addFunctionProperties):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::toString):
* runtime/JSFunction.h:
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::asStringConcurrently const):
* runtime/JSStringInlines.h:
* runtime/NativeExecutable.cpp:
(JSC::NativeExecutable::toStringSlow):
(JSC::NativeExecutable::visitChildrenImpl):
* runtime/NativeExecutable.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (278461 => 278462)


--- trunk/JSTests/ChangeLog	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/JSTests/ChangeLog	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1,3 +1,19 @@
+2021-06-04  Tadeu Zagallo  <[email protected]>
+
+        Optimize Function.prototype.toString
+        https://bugs.webkit.org/show_bug.cgi?id=226418
+        <rdar://77861846>
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/function-to-string.js: Added.
+        (f):
+        (C):
+        (C.prototype.method1):
+        (C.prototype.method2):
+        (test):
+        (test2):
+
 2021-06-03  Ross Kirsling  <[email protected]>
 
         [JSC] Implement JIT ICs for InByVal

Added: trunk/JSTests/microbenchmarks/function-to-string.js (0 => 278462)


--- trunk/JSTests/microbenchmarks/function-to-string.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/function-to-string.js	2021-06-04 15:58:13 UTC (rev 278462)
@@ -0,0 +1,40 @@
+function f(x, y, z) {
+    // comment in the body
+    const w = 42;
+    return w + x + y + z;
+
+}
+noInline(f);
+
+class C {
+    // comment in the class
+    constructor() {
+    }
+
+    method1() {
+        return "some string";
+    }
+
+    method2() {
+        return 42;
+    }
+}
+
+function test() {
+    f.toString();
+    C.toString();
+    print.toString();
+}
+noInline(test);
+
+for (let i = 0; i < 1e7; ++i)
+    test();
+
+function test2(x, y, z) {
+    for (let i = 0; i < 1e6; ++i) {
+        f(x.toString(), y.toString(), z.toString(), x.toString(), y.toString());
+    }
+}
+noInline(test2);
+
+test2(f, C, print);

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (278461 => 278462)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2021-06-04 15:58:13 UTC (rev 278462)
@@ -943,6 +943,7 @@
     runtime/JSArrayBufferViewInlines.h
     runtime/JSArrayIterator.h
     runtime/JSBigInt.h
+    runtime/JSBoundFunction.h
     runtime/JSCConfig.h
     runtime/JSCInlines.h
     runtime/JSCJSValue.h

Modified: trunk/Source/_javascript_Core/ChangeLog (278461 => 278462)


--- trunk/Source/_javascript_Core/ChangeLog	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1,3 +1,86 @@
+2021-06-04  Tadeu Zagallo  <[email protected]>
+
+        Optimize Function.prototype.toString
+        https://bugs.webkit.org/show_bug.cgi?id=226418
+        <rdar://77861846>
+
+        Reviewed by Saam Barati.
+
+        Add caching to Function.prototype.toString. This is used heavily in Speedometer2, and repeatedly recomputing a
+        string which is a constant is costly. We cache the results of toString in all cases except for bound functions.
+        To make this work for bound functions, we'd need to add a new field they can use for this cache. For other
+        functions, we cache it on the executable (either NativeExecutable or FunctionExecutable). The reason we can't
+        do this on the executable for bound functions is that all bound functions share the same executable, but
+        individual bound functions can have different names. The reason it's valid to cache the results in general is that a
+        function's name field can't be changed from JS code -- it's non-writable.
+
+        This patch also makes Function.prototype.toString an intrinsic in the DFG/FTL. We emit code on the fast path
+        which reads the cached value if it's present. If not, we call into the slow path, which will compute
+        the cached value for non bound functions, or compute the result for bound functions.
+
+        I added a new microbenchmark that speeds up by >35x:
+
+        function-to-string     2197.5952+-30.7118    ^     59.9861+-2.5550        ^ definitely 36.6350x faster
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::getExecutable):
+        (JSC::DFG::SpeculativeJIT::compileFunctionToString):
+        (JSC::DFG::SpeculativeJIT::compileGetExecutable):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::getExecutable):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetExecutable):
+        (JSC::FTL::DFG::LowerDFGToB3::compileFunctionToString):
+        * runtime/FunctionExecutable.cpp:
+        (JSC::FunctionExecutable::visitChildrenImpl):
+        (JSC::FunctionExecutable::toStringSlow):
+        * runtime/FunctionExecutable.h:
+        * runtime/FunctionExecutableInlines.h:
+        (JSC::FunctionExecutable::toString):
+        * runtime/FunctionPrototype.cpp:
+        (JSC::FunctionPrototype::addFunctionProperties):
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::toString):
+        * runtime/JSFunction.h:
+        * runtime/JSFunctionInlines.h:
+        (JSC::JSFunction::asStringConcurrently const):
+        * runtime/JSStringInlines.h:
+        * runtime/NativeExecutable.cpp:
+        (JSC::NativeExecutable::toStringSlow):
+        (JSC::NativeExecutable::visitChildrenImpl):
+        * runtime/NativeExecutable.h:
+
 2021-06-04  Michael Catanzaro  <[email protected]>
 
         Fix more GCC warnings

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (278461 => 278462)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1302,7 +1302,7 @@
 		86ECA3FA132DF25A002B2AD7 /* DFGScoreBoard.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3F9132DF25A002B2AD7 /* DFGScoreBoard.h */; };
 		86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */; };
 		86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */; };
-		86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; };
+		86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		8B3BF5E41E3D368B0076A87A /* AsyncGeneratorPrototype.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3BF5E31E3D365A0076A87A /* AsyncGeneratorPrototype.lut.h */; };
 		8B6016F61F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6016F41F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h */; };
 		8B9F6D561D5912FA001C739F /* IterationKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B9F6D551D5912FA001C739F /* IterationKind.h */; settings = {ATTRIBUTES = (Private, ); }; };

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -2855,6 +2855,19 @@
         break;
     }
 
+    case FunctionToString: {
+        JSValue value = m_state.forNode(node->child1()).value();
+        if (value) {
+            JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, value);
+            if (JSString* asString = function->asStringConcurrently(m_vm)) {
+                setConstant(node, *m_graph.freeze(asString));
+                break;
+            }
+        }
+        setForNode(node, m_vm.stringStructure.get());
+        break;
+    }
+
     case NumberToStringWithRadix: {
         JSValue radixValue = forNode(node->child2()).m_value;
         if (radixValue && radixValue.isInt32()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -3742,6 +3742,17 @@
 #endif
         }
 
+        case FunctionToStringIntrinsic: {
+            if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+                return false;
+
+            insertChecks();
+            Node* function = get(virtualRegisterForArgumentIncludingThis(0, registerOffset));
+            Node* resultNode = addToGraph(FunctionToString, function);
+            setResult(resultNode);
+            return true;
+        }
+
         default:
             return false;
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1818,6 +1818,10 @@
             RELEASE_ASSERT_NOT_REACHED();
             return;
         }
+
+    case FunctionToString:
+        def(PureValue(node));
+        return;
         
     case CountExecution:
     case SuperSamplerBegin:

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -290,6 +290,7 @@
     case DirectTailCall:
     case DirectTailCallInlinedCaller:
     case ForceOSRExit:
+    case FunctionToString:
     case GetById:
     case GetByIdDirect:
     case GetByIdDirectFlush:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1973,6 +1973,10 @@
             break;
         }
 
+        case FunctionToString: {
+            fixEdge<FunctionUse>(node->child1());
+            break;
+        }
 
         case SetPrivateBrand: {
             fixEdge<CellUse>(node->child1());

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -428,6 +428,7 @@
     macro(CallNumberConstructor, NodeResultJS | NodeMustGenerate) \
     macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
     macro(NumberToStringWithValidRadixConstant, NodeResultJS) \
+    macro(FunctionToString, NodeResultJS) \
     macro(MakeRope, NodeResultJS) \
     macro(InByVal, NodeResultBoolean | NodeMustGenerate) \
     macro(InById, NodeResultBoolean | NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -2560,6 +2560,15 @@
     return reinterpret_cast<char*>(numberToString(vm, value, radix));
 }
 
+JSC_DEFINE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject* globalObject, JSFunction* function))
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+
+    return function->toString(globalObject);
+}
+
 JSC_DEFINE_JIT_OPERATION(operationSingleCharacterString, JSString*, (VM* vmPointer, int32_t character))
 {
     VM& vm = *vmPointer;

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -246,6 +246,7 @@
 JSC_DECLARE_JIT_OPERATION(operationInt32ToStringWithValidRadix, char*, (JSGlobalObject*, int32_t, int32_t));
 JSC_DECLARE_JIT_OPERATION(operationInt52ToStringWithValidRadix, char*, (JSGlobalObject*, int64_t, int32_t));
 JSC_DECLARE_JIT_OPERATION(operationDoubleToStringWithValidRadix, char*, (JSGlobalObject*, double, int32_t));
+JSC_DECLARE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject*, JSFunction*));
 
 JSC_DECLARE_JIT_OPERATION(operationNormalizeMapKeyHeapBigInt, EncodedJSValue, (VM*, JSBigInt*));
 JSC_DECLARE_JIT_OPERATION(operationMapHash, UCPUStrictInt32, (JSGlobalObject*, EncodedJSValue input));

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1158,6 +1158,7 @@
         case StringCharAt:
         case CallStringConstructor:
         case ToString:
+        case FunctionToString:
         case NumberToStringWithRadix:
         case NumberToStringWithValidRadixConstant:
         case MakeRope:

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -273,6 +273,7 @@
     case ToBoolean:
     case LogicalNot:
     case ToString:
+    case FunctionToString:
     case NumberToStringWithValidRadixConstant:
     case StrCat:
     case CallStringConstructor:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -52,6 +52,7 @@
 #include "JSArrayIterator.h"
 #include "JSAsyncFunction.h"
 #include "JSAsyncGeneratorFunction.h"
+#include "JSBoundFunction.h"
 #include "JSCInlines.h"
 #include "JSGeneratorFunction.h"
 #include "JSImmutableButterfly.h"
@@ -10555,6 +10556,47 @@
     }
 }
 
+static void getExecutable(JITCompiler& jit, GPRReg functionGPR, GPRReg resultGPR)
+{
+    jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
+    auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
+    jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
+    hasExecutable.link(&jit);
+}
+
+void SpeculativeJIT::compileFunctionToString(Node* node)
+{
+    SpeculateCellOperand function(this, node->child1());
+    GPRTemporary executable(this);
+    GPRTemporary result(this);
+    JITCompiler::JumpList slowCases;
+
+    speculateFunction(node->child1(), function.gpr());
+
+    m_jit.emitLoadStructure(vm(), function.gpr(), result.gpr(), executable.gpr());
+    m_jit.loadPtr(JITCompiler::Address(result.gpr(), Structure::classInfoOffset()), result.gpr());
+    static_assert(std::is_final_v<JSBoundFunction>, "We don't handle subclasses when comparing classInfo below");
+    slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, result.gpr(), TrustedImmPtr(JSBoundFunction::info())));
+
+    getExecutable(m_jit, function.gpr(), executable.gpr());
+    JITCompiler::Jump isNativeExecutable = m_jit.branch8(JITCompiler::Equal, JITCompiler::Address(executable.gpr(), JSCell::typeInfoTypeOffset()), TrustedImm32(NativeExecutableType));
+
+    m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), FunctionExecutable::offsetOfRareData()), result.gpr());
+    slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
+    m_jit.loadPtr(MacroAssembler::Address(result.gpr(), FunctionExecutable::offsetOfAsStringInRareData()), result.gpr());
+    JITCompiler::Jump continuation = m_jit.jump();
+
+    isNativeExecutable.link(&m_jit);
+    m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), NativeExecutable::offsetOfAsString()), result.gpr());
+
+    continuation.link(&m_jit);
+    slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
+
+    addSlowPathGenerator(slowPathCall(slowCases, this, operationFunctionToString, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), function.gpr()));
+
+    cellResult(result.gpr(), node);
+}
+
 void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node)
 {
     compileNumberToStringWithValidRadixConstant(node, node->validRadixConstant());
@@ -13304,14 +13346,9 @@
 {
     SpeculateCellOperand function(this, node->child1());
     GPRTemporary result(this, Reuse, function);
-    GPRReg functionGPR = function.gpr();
-    GPRReg resultGPR = result.gpr();
-    speculateCellType(node->child1(), functionGPR, SpecFunction, JSFunctionType);
-    m_jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
-    auto hasExecutable = m_jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
-    m_jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
-    hasExecutable.link(&m_jit);
-    cellResult(resultGPR, node);
+    speculateFunction(node->child1(), function.gpr());
+    getExecutable(m_jit, function.gpr(), result.gpr());
+    cellResult(result.gpr(), node);
 }
 
 void SpeculativeJIT::compileGetGetter(Node* node)

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -1227,6 +1227,7 @@
     void emitSwitch(Node*);
     
     void compileToStringOrCallStringConstructorOrStringValueOf(Node*);
+    void compileFunctionToString(Node*);
     void compileNumberToStringWithRadix(Node*);
     void compileNumberToStringWithValidRadixConstant(Node*);
     void compileNumberToStringWithValidRadixConstant(Node*, int32_t radix);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -3205,6 +3205,10 @@
         compileToStringOrCallStringConstructorOrStringValueOf(node);
         break;
     }
+
+    case FunctionToString:
+        compileFunctionToString(node);
+        break;
         
     case NewStringObject: {
         compileNewStringObject(node);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -3790,6 +3790,10 @@
         compileToStringOrCallStringConstructorOrStringValueOf(node);
         break;
     }
+
+    case FunctionToString:
+        compileFunctionToString(node);
+        break;
         
     case NewStringObject: {
         compileNewStringObject(node);

Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (278461 => 278462)


--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -77,6 +77,8 @@
     macro(DirectArguments_minCapacity, DirectArguments::offsetOfMinCapacity()) \
     macro(DirectArguments_mappedArguments, DirectArguments::offsetOfMappedArguments()) \
     macro(DirectArguments_modifiedArgumentsDescriptor, DirectArguments::offsetOfModifiedArgumentsDescriptor()) \
+    macro(FunctionExecutable_rareData, FunctionExecutable::offsetOfRareData()) \
+    macro(FunctionExecutableRareData_asString, FunctionExecutable::offsetOfAsStringInRareData()) \
     macro(FunctionRareData_allocator, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfAllocator()) \
     macro(FunctionRareData_structure, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfStructure()) \
     macro(FunctionRareData_prototype, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfPrototype()) \
@@ -119,6 +121,7 @@
     macro(JSRopeString_fiber2, JSRopeString::offsetOfFiber2()) \
     macro(JSScope_next, JSScope::offsetOfNext()) \
     macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
+    macro(NativeExecutable_asString, NativeExecutable::offsetOfAsString()) \
     macro(RegExpObject_regExpAndLastIndexIsNotWritableFlag, RegExpObject::offsetOfRegExpAndLastIndexIsNotWritableFlag()) \
     macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
     macro(ShadowChicken_Packet_callee, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)) \

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -217,6 +217,7 @@
     case ToNumber:
     case ToNumeric:
     case ToString:
+    case FunctionToString:
     case ToObject:
     case CallObjectConstructor:
     case CallStringConstructor:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -77,6 +77,7 @@
 #include "JSAsyncFunction.h"
 #include "JSAsyncGenerator.h"
 #include "JSAsyncGeneratorFunction.h"
+#include "JSBoundFunction.h"
 #include "JSCInlines.h"
 #include "JSGenerator.h"
 #include "JSGeneratorFunction.h"
@@ -1196,6 +1197,9 @@
         case StringValueOf:
             compileToStringOrCallStringConstructorOrStringValueOf();
             break;
+        case FunctionToString:
+            compileFunctionToString();
+            break;
         case ToPrimitive:
             compileToPrimitive();
             break;
@@ -3774,14 +3778,12 @@
         speculate(BadIdent, noValue(), nullptr, m_out.notEqual(stringImpl, m_out.constIntPtr(uid)));
     }
 
-    void compileGetExecutable()
+    LValue getExecutable(LValue function)
     {
         LBasicBlock continuation = m_out.newBlock();
         LBasicBlock hasRareData = m_out.newBlock();
-        LValue cell = lowCell(m_node->child1());
-        speculateFunction(m_node->child1(), cell);
 
-        LValue rareDataTags = m_out.loadPtr(cell, m_heaps.JSFunction_executableOrRareData);
+        LValue rareDataTags = m_out.loadPtr(function, m_heaps.JSFunction_executableOrRareData);
         ValueFromBlock fastExecutable = m_out.anchor(rareDataTags);
         m_out.branch(m_out.testIsZeroPtr(rareDataTags, m_out.constIntPtr(JSFunction::rareDataTag)), unsure(continuation), unsure(hasRareData));
 
@@ -3791,8 +3793,16 @@
         m_out.jump(continuation);
 
         m_out.appendTo(continuation, lastNext);
-        setJSValue(m_out.phi(pointerType(), fastExecutable, slowExecutable));
+        return m_out.phi(pointerType(), fastExecutable, slowExecutable);
     }
+
+    void compileGetExecutable()
+    {
+        LValue cell = lowCell(m_node->child1());
+        speculateFunction(m_node->child1(), cell);
+        LValue executable = getExecutable(cell);
+        setJSValue(executable);
+    }
     
     void compileArrayify()
     {
@@ -8220,6 +8230,55 @@
             break;
         }
     }
+
+    void compileFunctionToString()
+    {
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
+
+        LBasicBlock notBoundFunctionCase = m_out.newBlock();
+        LBasicBlock functionExecutableCase = m_out.newBlock();
+        LBasicBlock nativeExecutableCase = m_out.newBlock();
+        LBasicBlock testPtr = m_out.newBlock();
+        LBasicBlock hasRareData = m_out.newBlock();
+        LBasicBlock slowCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        LValue function = lowCell(m_node->child1());
+        speculateFunction(m_node->child1(), function);
+
+        LValue structure = loadStructure(function);
+        LValue classInfo = m_out.loadPtr(structure, m_heaps.Structure_classInfo);
+        static_assert(std::is_final_v<JSBoundFunction>, "We don't handle subclasses when comparing classInfo below");
+        m_out.branch(m_out.equal(classInfo, m_out.constIntPtr(JSBoundFunction::info())), unsure(slowCase), unsure(notBoundFunctionCase));
+
+        LBasicBlock lastNext = m_out.appendTo(notBoundFunctionCase, nativeExecutableCase);
+        LValue executable = getExecutable(function);
+        m_out.branch(isType(executable, NativeExecutableType), unsure(nativeExecutableCase), unsure(functionExecutableCase));
+
+        m_out.appendTo(nativeExecutableCase, functionExecutableCase);
+        ValueFromBlock nativeResult = m_out.anchor(m_out.loadPtr(executable, m_heaps.NativeExecutable_asString));
+        m_out.jump(testPtr);
+
+        m_out.appendTo(functionExecutableCase, testPtr);
+        LValue rareData = m_out.loadPtr(executable, m_heaps.FunctionExecutable_rareData);
+        m_out.branch(m_out.notNull(rareData), usually(hasRareData), rarely(slowCase));
+
+        m_out.appendTo(hasRareData, slowCase);
+        ValueFromBlock functionResult = m_out.anchor(m_out.loadPtr(rareData, m_heaps.FunctionExecutableRareData_asString));
+        m_out.jump(testPtr);
+
+        m_out.appendTo(testPtr, continuation);
+        LValue asString = m_out.phi(pointerType(), nativeResult, functionResult);
+        ValueFromBlock fastResult = m_out.anchor(asString);
+        m_out.branch(m_out.notNull(asString), usually(continuation), rarely(slowCase));
+
+        m_out.appendTo(slowCase, continuation);
+        ValueFromBlock slowResult = m_out.anchor(vmCall(pointerType(), operationFunctionToString, weakPointer(globalObject), function));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setJSValue(m_out.phi(pointerType(), fastResult, slowResult));
+    }
     
     void compileToPrimitive()
     {

Modified: trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -79,6 +79,7 @@
     visitor.append(thisObject->m_unlinkedExecutable);
     if (RareData* rareData = thisObject->m_rareData.get()) {
         visitor.append(rareData->m_cachedPolyProtoStructure);
+        visitor.append(rareData->m_asString);
         if (TemplateObjectMap* map = rareData->m_templateObjectMap.get()) {
             Locker locker { thisObject->cellLock() };
             for (auto& entry : *map)
@@ -116,6 +117,81 @@
     return *m_rareData;
 }
 
+JSString* FunctionExecutable::toStringSlow(JSGlobalObject* globalObject)
+{
+    VM& vm = getVM(globalObject);
+    ASSERT(m_rareData && !m_rareData->m_asString);
+
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    const auto& cache = [&](JSString* asString) {
+        WTF::storeStoreFence();
+        m_rareData->m_asString.set(vm, this, asString);
+        return asString;
+    };
+
+    const auto& cacheIfNoException = [&](JSValue value) -> JSString* {
+        RETURN_IF_EXCEPTION(throwScope, nullptr);
+        return cache(::JSC::asString(value));
+    };
+
+    if (isBuiltinFunction())
+        return cacheIfNoException(jsMakeNontrivialString(globalObject, "function ", name().string(), "() {\n    [native code]\n}"));
+
+    if (isClass())
+        return cache(jsString(vm, classSource().view().toString()));
+
+    String functionHeader;
+    switch (parseMode()) {
+    case SourceParseMode::GeneratorWrapperFunctionMode:
+    case SourceParseMode::GeneratorWrapperMethodMode:
+        functionHeader = "function* ";
+        break;
+
+    case SourceParseMode::NormalFunctionMode:
+    case SourceParseMode::GetterMode:
+    case SourceParseMode::SetterMode:
+    case SourceParseMode::MethodMode:
+    case SourceParseMode::ProgramMode:
+    case SourceParseMode::ModuleAnalyzeMode:
+    case SourceParseMode::ModuleEvaluateMode:
+    case SourceParseMode::GeneratorBodyMode:
+    case SourceParseMode::AsyncGeneratorBodyMode:
+    case SourceParseMode::AsyncFunctionBodyMode:
+    case SourceParseMode::AsyncArrowFunctionBodyMode:
+        functionHeader = "function ";
+        break;
+
+    case SourceParseMode::ArrowFunctionMode:
+    case SourceParseMode::ClassFieldInitializerMode:
+        functionHeader = "";
+        break;
+
+    case SourceParseMode::AsyncFunctionMode:
+    case SourceParseMode::AsyncMethodMode:
+        functionHeader = "async function ";
+        break;
+
+    case SourceParseMode::AsyncArrowFunctionMode:
+        functionHeader = "async ";
+        break;
+
+    case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
+    case SourceParseMode::AsyncGeneratorWrapperMethodMode:
+        functionHeader = "async function* ";
+        break;
+    }
+
+    StringView src = ""
+        parametersStartOffset(),
+        parametersStartOffset() + source().length());
+
+    String name = this->name().string();
+    if (name == vm.propertyNames->starDefaultPrivateName.string())
+        name = emptyString();
+    return cacheIfNoException(jsMakeNontrivialString(globalObject, functionHeader, name, src));
+}
+
 void FunctionExecutable::overrideInfo(const FunctionOverrideInfo& overrideInfo)
 {
     auto& rareData = ensureRareData();

Modified: trunk/Source/_javascript_Core/runtime/FunctionExecutable.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/FunctionExecutable.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/FunctionExecutable.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -285,6 +285,17 @@
 
     void finalizeUnconditionally(VM&);
 
+    JSString* toString(JSGlobalObject*);
+    JSString* asStringConcurrently() const
+    {
+        if (!m_rareData)
+            return nullptr;
+        return m_rareData->m_asString.get();
+    }
+
+    static inline ptrdiff_t offsetOfRareData() { return OBJECT_OFFSETOF(FunctionExecutable, m_rareData); }
+    static inline ptrdiff_t offsetOfAsStringInRareData() { return OBJECT_OFFSETOF(RareData, m_asString); }
+
 private:
     friend class ExecutableBase;
     FunctionExecutable(VM&, const SourceCode&, UnlinkedFunctionExecutable*, Intrinsic, bool isInsideOrdinaryFunction);
@@ -304,6 +315,7 @@
         unsigned m_typeProfilingEndOffset { UINT_MAX };
         std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
         WriteBarrier<Structure> m_cachedPolyProtoStructure;
+        WriteBarrier<JSString> m_asString;
     };
 
     RareData& ensureRareData()
@@ -314,6 +326,8 @@
     }
     RareData& ensureRareDataSlow();
 
+    JSString* toStringSlow(JSGlobalObject*);
+
     // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent.
     // If RareData is required, materialize RareData, swap it, and store top-level executable pointer inside RareData.
     // https://bugs.webkit.org/show_bug.cgi?id=197625

Modified: trunk/Source/_javascript_Core/runtime/FunctionExecutableInlines.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/FunctionExecutableInlines.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/FunctionExecutableInlines.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -35,5 +35,13 @@
     m_singleton.finalizeUnconditionally(vm);
 }
 
+JSString* FunctionExecutable::toString(JSGlobalObject* globalObject)
+{
+    RareData& rareData = ensureRareData();
+    if (!rareData.m_asString)
+        return toStringSlow(globalObject);
+    return rareData.m_asString.get();
+}
+
 } // namespace JSC
 

Modified: trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -54,8 +54,7 @@
 
 void FunctionPrototype::addFunctionProperties(VM& vm, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction, JSFunction** hasInstanceSymbolFunction)
 {
-    JSFunction* toStringFunction = JSFunction::create(vm, globalObject, 0, vm.propertyNames->toString.string(), functionProtoFuncToString);
-    putDirectWithoutTransition(vm, vm.propertyNames->toString, toStringFunction, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().toStringPublicName(), functionProtoFuncToString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, FunctionToStringIntrinsic);
 
     *applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
     *callFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().callPublicName(), functionPrototypeCallCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
@@ -81,58 +80,7 @@
     if (thisValue.inherits<JSFunction>(vm)) {
         JSFunction* function = jsCast<JSFunction*>(thisValue);
         Integrity::auditStructureID(vm, function->structureID());
-        if (function->isHostOrBuiltinFunction())
-            RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "function ", function->name(vm), "() {\n    [native code]\n}")));
-
-        FunctionExecutable* executable = function->jsExecutable();
-        if (executable->isClass())
-            return JSValue::encode(jsString(vm, executable->classSource().view().toString()));
-
-        String functionHeader;
-        switch (executable->parseMode()) {
-        case SourceParseMode::GeneratorWrapperFunctionMode:
-        case SourceParseMode::GeneratorWrapperMethodMode:
-            functionHeader = "function* ";
-            break;
-
-        case SourceParseMode::NormalFunctionMode:
-        case SourceParseMode::GetterMode:
-        case SourceParseMode::SetterMode:
-        case SourceParseMode::MethodMode:
-        case SourceParseMode::ProgramMode:
-        case SourceParseMode::ModuleAnalyzeMode:
-        case SourceParseMode::ModuleEvaluateMode:
-        case SourceParseMode::GeneratorBodyMode:
-        case SourceParseMode::AsyncGeneratorBodyMode:
-        case SourceParseMode::AsyncFunctionBodyMode:
-        case SourceParseMode::AsyncArrowFunctionBodyMode:
-            functionHeader = "function ";
-            break;
-
-        case SourceParseMode::ArrowFunctionMode:
-        case SourceParseMode::ClassFieldInitializerMode:
-            functionHeader = "";
-            break;
-
-        case SourceParseMode::AsyncFunctionMode:
-        case SourceParseMode::AsyncMethodMode:
-            functionHeader = "async function ";
-            break;
-
-        case SourceParseMode::AsyncArrowFunctionMode:
-            functionHeader = "async ";
-            break;
-
-        case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
-        case SourceParseMode::AsyncGeneratorWrapperMethodMode:
-            functionHeader = "async function* ";
-            break;
-        }
-
-        StringView source = executable->source().provider()->getRange(
-            executable->parametersStartOffset(),
-            executable->parametersStartOffset() + executable->source().length());
-        RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, functionHeader, function->name(vm), source)));
+        RELEASE_AND_RETURN(scope, JSValue::encode(function->toString(globalObject)));
     }
 
     if (thisValue.inherits<InternalFunction>(vm)) {

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -275,6 +275,8 @@
         return "AtomicsXorIntrinsic";
     case ParseIntIntrinsic:
         return "ParseIntIntrinsic";
+    case FunctionToStringIntrinsic:
+        return "FunctionToStringIntrinsic";
     case TypedArrayLengthIntrinsic:
         return "TypedArrayLengthIntrinsic";
     case TypedArrayByteLengthIntrinsic:

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -152,6 +152,7 @@
     AtomicsWaitIntrinsic,
     AtomicsXorIntrinsic,
     ParseIntIntrinsic,
+    FunctionToStringIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/JSFunction.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -247,6 +247,22 @@
     return jsExecutable()->ecmaName().string();
 }
 
+JSString* JSFunction::toString(JSGlobalObject* globalObject)
+{
+    VM& vm = getVM(globalObject);
+    if (inherits<JSBoundFunction>(vm)) {
+        JSBoundFunction* function = jsCast<JSBoundFunction*>(this);
+        auto scope = DECLARE_THROW_SCOPE(vm);
+        JSValue string = jsMakeNontrivialString(globalObject, "function ", function->nameString(), "() {\n    [native code]\n}");
+        RETURN_IF_EXCEPTION(scope, nullptr);
+        return asString(string);
+    }
+
+    if (isHostFunction())
+        return static_cast<NativeExecutable*>(executable())->toString(globalObject);
+    return jsExecutable()->toString(globalObject);
+}
+
 const SourceCode* JSFunction::sourceCode() const
 {
     if (isHostOrBuiltinFunction())

Modified: trunk/Source/_javascript_Core/runtime/JSFunction.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/JSFunction.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -90,7 +90,10 @@
     JS_EXPORT_PRIVATE String name(VM&);
     JS_EXPORT_PRIVATE String displayName(VM&);
     JS_EXPORT_PRIVATE const String calculatedDisplayName(VM&);
+    JS_EXPORT_PRIVATE JSString* toString(JSGlobalObject*);
 
+    JSString* asStringConcurrently(VM&) const;
+
     ExecutableBase* executable() const
     {
         uintptr_t executableOrRareData = m_executableOrRareData;

Modified: trunk/Source/_javascript_Core/runtime/JSFunctionInlines.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/JSFunctionInlines.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/JSFunctionInlines.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "FunctionExecutable.h"
+#include "JSBoundFunction.h"
 #include "JSFunction.h"
 #include "NativeExecutable.h"
 
@@ -156,4 +157,13 @@
     return rareData;
 }
 
+inline JSString* JSFunction::asStringConcurrently(VM& vm) const
+{
+    if (inherits<JSBoundFunction>(vm))
+        return nullptr;
+    if (isHostFunction())
+        return static_cast<NativeExecutable*>(executable())->asStringConcurrently();
+    return jsExecutable()->asStringConcurrently();
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSStringInlines.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/JSStringInlines.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/JSStringInlines.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "JSGlobalObjectInlines.h"
 #include "JSString.h"
 
 namespace JSC {

Modified: trunk/Source/_javascript_Core/runtime/NativeExecutable.cpp (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/NativeExecutable.cpp	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/NativeExecutable.cpp	2021-06-04 15:58:13 UTC (rev 278462)
@@ -93,4 +93,31 @@
     return CodeBlockHash(bitwise_cast<uintptr_t>(m_constructor));
 }
 
+JSString* NativeExecutable::toStringSlow(JSGlobalObject *globalObject)
+{
+    VM& vm = getVM(globalObject);
+
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue value = jsMakeNontrivialString(globalObject, "function ", name(), "() {\n    [native code]\n}");
+
+    RETURN_IF_EXCEPTION(throwScope, nullptr);
+
+    JSString* asString = ::JSC::asString(value);
+    WTF::storeStoreFence();
+    m_asString.set(vm, this, asString);
+    return asString;
+}
+
+template<typename Visitor>
+void NativeExecutable::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+    NativeExecutable* thisObject = jsCast<NativeExecutable*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(thisObject->m_asString);
+}
+
+DEFINE_VISIT_CHILDREN(NativeExecutable);
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/NativeExecutable.h (278461 => 278462)


--- trunk/Source/_javascript_Core/runtime/NativeExecutable.h	2021-06-04 15:48:55 UTC (rev 278461)
+++ trunk/Source/_javascript_Core/runtime/NativeExecutable.h	2021-06-04 15:58:13 UTC (rev 278462)
@@ -67,6 +67,7 @@
         return OBJECT_OFFSETOF(NativeExecutable, m_constructor);
     }
 
+    DECLARE_VISIT_CHILDREN;
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue proto);
         
     DECLARE_INFO;
@@ -76,14 +77,27 @@
     const DOMJIT::Signature* signatureFor(CodeSpecializationKind) const;
     Intrinsic intrinsic() const;
 
+    JSString* toString(JSGlobalObject* globalObject)
+    {
+        if (!m_asString)
+            return toStringSlow(globalObject);
+        return m_asString.get();
+    }
+
+    JSString* asStringConcurrently() const { return m_asString.get(); }
+    static inline ptrdiff_t offsetOfAsString() { return OBJECT_OFFSETOF(NativeExecutable, m_asString); }
+
 private:
     NativeExecutable(VM&, TaggedNativeFunction, TaggedNativeFunction constructor);
     void finishCreation(VM&, Ref<JITCode>&& callThunk, Ref<JITCode>&& constructThunk, const String& name);
 
+    JSString* toStringSlow(JSGlobalObject*);
+
     TaggedNativeFunction m_function;
     TaggedNativeFunction m_constructor;
 
     String m_name;
+    WriteBarrier<JSString> m_asString;
 };
 
 } // namespace JSC
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to