TypeOf should be fast
https://bugs.webkit.org/show_bug.cgi?id=144396
Reviewed by Geoffrey Garen.
Adds comprehensive support for fast typeof to the optimizing JITs. Calls into the runtime
are only used for very exotic objects - they must have either the MasqueradesAsUndefined or
TypeOfShouldCallGetCallData type flags set. All other cases are handled inline.
This means optimizing IsObjectOrNull, IsFunction, and TypeOf - all node types that used to
rely heavily on C++ calls to fulfill their function.
Because TypeOf is now so fast, we no longer need to do any speculations on this node.
In the FTL, we take this further by querying AI for each branch in the TypeOf decision tree.
This means that if the TypeOf is dominated by any type checks, we will automatically prune
out cases that are redundant.
This patch anticipates the addition of SwitchTypeOf or something like that. So, the TypeOf
code generation is designed to be reusable.
This is a speed-up on most typeof benchmarks. But, it is a slow-down on benchmarks that take
the exotic call trap hook. That hook is now in a deeper slow path than before.
* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize): TypeOf was pure all along, but we failed to realize this.
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileIsObjectOrNull):
(JSC::DFG::SpeculativeJIT::compileIsFunction):
(JSC::DFG::SpeculativeJIT::compileTypeOf):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::blessedBooleanResult):
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileIsObjectOrNull):
(JSC::FTL::LowerDFGToLLVM::compileIsFunction):
(JSC::FTL::LowerDFGToLLVM::compileTypeOf):
(JSC::FTL::LowerDFGToLLVM::buildTypeOf): Reusable TypeOf building for the FTL.
(JSC::FTL::LowerDFGToLLVM::isExoticForTypeof):
* ftl/FTLSwitchCase.h:
(JSC::FTL::SwitchCase::SwitchCase):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfNotEqual):
(JSC::AssemblyHelpers::branchIfEqual):
(JSC::AssemblyHelpers::branchIfNumber):
(JSC::AssemblyHelpers::branchIfNotNumber):
(JSC::AssemblyHelpers::branchIfBoolean):
(JSC::AssemblyHelpers::branchIfNotBoolean):
(JSC::AssemblyHelpers::boxBooleanPayload):
(JSC::AssemblyHelpers::boxBoolean):
(JSC::AssemblyHelpers::emitTypeOf): Reusable TypeOf building for assembly JITs.
* jit/JITOperations.h:
* runtime/SmallStrings.h:
(JSC::SmallStrings::typeString):
* runtime/TypeofType.cpp: Added.
(WTF::printInternal):
* runtime/TypeofType.h: Added.
* tests/stress/type-of-functions-and-objects.js: Modified this test to give more comprehensive feedback.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@183724 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 550e8d6..e512418 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -581,6 +581,7 @@
runtime/TypeSet.cpp
runtime/TypedArrayController.cpp
runtime/TypedArrayType.cpp
+ runtime/TypeofType.cpp
runtime/VM.cpp
runtime/VMEntryScope.cpp
runtime/VarOffset.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 79d6c3c..d756382 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,82 @@
+2015-05-01 Filip Pizlo <fpizlo@apple.com>
+
+ TypeOf should be fast
+ https://bugs.webkit.org/show_bug.cgi?id=144396
+
+ Reviewed by Geoffrey Garen.
+
+ Adds comprehensive support for fast typeof to the optimizing JITs. Calls into the runtime
+ are only used for very exotic objects - they must have either the MasqueradesAsUndefined or
+ TypeOfShouldCallGetCallData type flags set. All other cases are handled inline.
+
+ This means optimizing IsObjectOrNull, IsFunction, and TypeOf - all node types that used to
+ rely heavily on C++ calls to fulfill their function.
+
+ Because TypeOf is now so fast, we no longer need to do any speculations on this node.
+
+ In the FTL, we take this further by querying AI for each branch in the TypeOf decision tree.
+ This means that if the TypeOf is dominated by any type checks, we will automatically prune
+ out cases that are redundant.
+
+ This patch anticipates the addition of SwitchTypeOf or something like that. So, the TypeOf
+ code generation is designed to be reusable.
+
+ This is a speed-up on most typeof benchmarks. But, it is a slow-down on benchmarks that take
+ the exotic call trap hook. That hook is now in a deeper slow path than before.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize): TypeOf was pure all along, but we failed to realize this.
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGHeapLocation.cpp:
+ (WTF::printInternal):
+ * dfg/DFGHeapLocation.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileIsObjectOrNull):
+ (JSC::DFG::SpeculativeJIT::compileIsFunction):
+ (JSC::DFG::SpeculativeJIT::compileTypeOf):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::blessedBooleanResult):
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLIntrinsicRepository.h:
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::LowerDFGToLLVM::compileIsObjectOrNull):
+ (JSC::FTL::LowerDFGToLLVM::compileIsFunction):
+ (JSC::FTL::LowerDFGToLLVM::compileTypeOf):
+ (JSC::FTL::LowerDFGToLLVM::buildTypeOf): Reusable TypeOf building for the FTL.
+ (JSC::FTL::LowerDFGToLLVM::isExoticForTypeof):
+ * ftl/FTLSwitchCase.h:
+ (JSC::FTL::SwitchCase::SwitchCase):
+ * jit/AssemblyHelpers.h:
+ (JSC::AssemblyHelpers::branchIfNotEqual):
+ (JSC::AssemblyHelpers::branchIfEqual):
+ (JSC::AssemblyHelpers::branchIfNumber):
+ (JSC::AssemblyHelpers::branchIfNotNumber):
+ (JSC::AssemblyHelpers::branchIfBoolean):
+ (JSC::AssemblyHelpers::branchIfNotBoolean):
+ (JSC::AssemblyHelpers::boxBooleanPayload):
+ (JSC::AssemblyHelpers::boxBoolean):
+ (JSC::AssemblyHelpers::emitTypeOf): Reusable TypeOf building for assembly JITs.
+ * jit/JITOperations.h:
+ * runtime/SmallStrings.h:
+ (JSC::SmallStrings::typeString):
+ * runtime/TypeofType.cpp: Added.
+ (WTF::printInternal):
+ * runtime/TypeofType.h: Added.
+ * tests/stress/type-of-functions-and-objects.js: Modified this test to give more comprehensive feedback.
+
2015-05-02 Filip Pizlo <fpizlo@apple.com>
Unreviewed, add a FIXME referencing https://bugs.webkit.org/show_bug.cgi?id=144527.
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index febde27..a555ae9 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -841,6 +841,7 @@
<ClCompile Include="..\runtime\TestRunnerUtils.cpp" />
<ClCompile Include="..\runtime\TypedArrayController.cpp" />
<ClCompile Include="..\runtime\TypedArrayType.cpp" />
+ <ClCompile Include="..\runtime\TypeofType.cpp" />
<ClCompile Include="..\runtime\TypeLocationCache.cpp" />
<ClCompile Include="..\runtime\TypeProfiler.cpp" />
<ClCompile Include="..\runtime\TypeProfilerLog.cpp" />
@@ -1688,6 +1689,7 @@
<ClInclude Include="..\runtime\TypedArrayController.h" />
<ClInclude Include="..\runtime\TypedArrayInlines.h" />
<ClInclude Include="..\runtime\TypedArrayType.h" />
+ <ClInclude Include="..\runtime\TypeofType.h" />
<ClInclude Include="..\runtime\TypeLocationCache.h" />
<ClInclude Include="..\runtime\TypeProfiler.h" />
<ClInclude Include="..\runtime\TypeProfilerLog.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 1a2b460..1f04f35 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -726,6 +726,8 @@
0FF922D414F46B410041A24E /* LLIntOffsetsExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4680A114BA7F8200BFE272 /* LLIntOffsetsExtractor.cpp */; };
0FFA549716B8835000B3A982 /* A64DOpcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 652A3A221651C69700A80AFE /* A64DOpcode.cpp */; };
0FFA549816B8835300B3A982 /* A64DOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 652A3A231651C69700A80AFE /* A64DOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0FFB6C381AF48DDC00DB1BF7 /* TypeofType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFB6C361AF48DDC00DB1BF7 /* TypeofType.cpp */; };
+ 0FFB6C391AF48DDC00DB1BF7 /* TypeofType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFB6C371AF48DDC00DB1BF7 /* TypeofType.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFB921816D02EB20055A5DB /* DFGAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4B51916B62772003F696B /* DFGAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFB921A16D02EC50055A5DB /* DFGBasicBlockInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD5652216AB780A00197653 /* DFGBasicBlockInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFB921B16D02F010055A5DB /* DFGNodeAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4B51F16B62772003F696B /* DFGNodeAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2462,6 +2464,8 @@
0FF8BDE81AD4CF7100DFE884 /* InferredValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InferredValue.cpp; sourceTree = "<group>"; };
0FF8BDE91AD4CF7100DFE884 /* InferredValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InferredValue.h; sourceTree = "<group>"; };
0FF922CF14F46B130041A24E /* JSCLLIntOffsetsExtractor */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = JSCLLIntOffsetsExtractor; sourceTree = BUILT_PRODUCTS_DIR; };
+ 0FFB6C361AF48DDC00DB1BF7 /* TypeofType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TypeofType.cpp; sourceTree = "<group>"; };
+ 0FFB6C371AF48DDC00DB1BF7 /* TypeofType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TypeofType.h; sourceTree = "<group>"; };
0FFC99D0184EC8AD009C10AB /* ConstantMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantMode.h; sourceTree = "<group>"; };
0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBufferNeuteringWatchpoint.cpp; sourceTree = "<group>"; };
0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayBufferNeuteringWatchpoint.h; sourceTree = "<group>"; };
@@ -4451,9 +4455,9 @@
BC9BB95B0E19680600DF8855 /* InternalFunction.cpp */,
BC11667A0E199C05008066DD /* InternalFunction.h */,
86BF642A148DB2B5004DE36A /* Intrinsic.h */,
+ FE4D55B71AE716CA0052E459 /* IterationStatus.h */,
70113D491A8DB093003848C4 /* IteratorOperations.cpp */,
70113D4A1A8DB093003848C4 /* IteratorOperations.h */,
- FE4D55B71AE716CA0052E459 /* IterationStatus.h */,
93ADFCE60CCBD7AC00D30B08 /* JSArray.cpp */,
938772E5038BFE19008635CE /* JSArray.h */,
0F2B66B417B6B5AB00A7AE3F /* JSArrayBuffer.cpp */,
@@ -4739,6 +4743,8 @@
0F2B66DD17B6B5AB00A7AE3F /* TypedArrayType.h */,
52B310FE1975B4240080857C /* TypeLocationCache.cpp */,
52B311001975B4670080857C /* TypeLocationCache.h */,
+ 0FFB6C361AF48DDC00DB1BF7 /* TypeofType.cpp */,
+ 0FFB6C371AF48DDC00DB1BF7 /* TypeofType.h */,
52C952B819A28A1C0069B386 /* TypeProfiler.cpp */,
52C952B619A289850069B386 /* TypeProfiler.h */,
0F2D4DDF19832D91007D4B19 /* TypeProfilerLog.cpp */,
@@ -5711,6 +5717,7 @@
BC18C3F40E16F5CD00B34460 /* Completion.h in Headers */,
0FDB2CEA174896C7007B3C1B /* ConcurrentJITLock.h in Headers */,
BC18C3F50E16F5CD00B34460 /* config.h in Headers */,
+ 0FFB6C391AF48DDC00DB1BF7 /* TypeofType.h in Headers */,
144836E7132DA7BE005BE785 /* ConservativeRoots.h in Headers */,
A5FD007A189B051000633231 /* ConsoleMessage.h in Headers */,
A5FD0074189B038C00633231 /* ConsoleTypes.h in Headers */,
@@ -7361,6 +7368,7 @@
147F39D4107EC37600427A48 /* JSObject.cpp in Sources */,
1482B7E40A43076000517CFC /* JSObjectRef.cpp in Sources */,
A7F993600FD7325100A0B2D0 /* JSONObject.cpp in Sources */,
+ 0FFB6C381AF48DDC00DB1BF7 /* TypeofType.cpp in Sources */,
95F6E6950E5B5F970091E860 /* JSProfilerPrivate.cpp in Sources */,
7C184E1A17BEDBD3007CB63A /* JSPromise.cpp in Sources */,
7C184E2217BEE240007CB63A /* JSPromiseConstructor.cpp in Sources */,
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index d38579e..e87c38a 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -159,6 +159,7 @@
case ValueToInt32:
case GetExecutable:
case BottomValue:
+ case TypeOf:
def(PureValue(node));
return;
@@ -362,11 +363,6 @@
def(HeapLocation(IsFunctionLoc, MiscFields, node->child1()), node);
return;
- case TypeOf:
- read(MiscFields);
- def(HeapLocation(TypeOfLoc, MiscFields, node->child1()), node);
- return;
-
case GetById:
case GetByIdFlush:
case PutById:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 652cb6a..15dcd41 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -346,14 +346,6 @@
break;
}
- case TypeOf: {
- if (node->child1()->shouldSpeculateString())
- fixEdge<StringUse>(node->child1());
- else if (node->child1()->shouldSpeculateCell())
- fixEdge<CellUse>(node->child1());
- break;
- }
-
case CompareEqConstant: {
break;
}
@@ -1241,6 +1233,7 @@
case MovHint:
case ZombieHint:
case BottomValue:
+ case TypeOf:
break;
#else
default:
diff --git a/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp b/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
index db693d4..2ca344a 100644
--- a/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
+++ b/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
@@ -68,10 +68,6 @@
out.print("IsFunctionLoc");
return;
- case TypeOfLoc:
- out.print("TypeOfLoc");
- return;
-
case GetterLoc:
out.print("GetterLoc");
return;
diff --git a/Source/JavaScriptCore/dfg/DFGHeapLocation.h b/Source/JavaScriptCore/dfg/DFGHeapLocation.h
index f7c119b..ce27ef4 100644
--- a/Source/JavaScriptCore/dfg/DFGHeapLocation.h
+++ b/Source/JavaScriptCore/dfg/DFGHeapLocation.h
@@ -53,7 +53,6 @@
NamedPropertyLoc,
SetterLoc,
StructureLoc,
- TypeOfLoc,
TypedArrayByteOffsetLoc,
VarInjectionWatchpointLoc,
StackLoc,
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 64a4534..8bb2d8d 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -860,21 +860,84 @@
return result;
}
-size_t JIT_OPERATION operationIsObjectOrNull(ExecState* exec, EncodedJSValue value)
-{
- return jsIsObjectTypeOrNull(exec, JSValue::decode(value));
-}
-
-size_t JIT_OPERATION operationIsFunction(EncodedJSValue value)
-{
- return jsIsFunctionType(JSValue::decode(value));
-}
-
-JSCell* JIT_OPERATION operationTypeOf(ExecState* exec, JSCell* value)
+size_t JIT_OPERATION operationObjectIsObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- return jsTypeStringForValue(exec, JSValue(value)).asCell();
+
+ ASSERT(jsDynamicCast<JSObject*>(object));
+
+ if (object->structure(vm)->masqueradesAsUndefined(globalObject))
+ return false;
+ if (object->type() == JSFunctionType)
+ return false;
+ if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) {
+ CallData callData;
+ if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone)
+ return false;
+ }
+
+ return true;
+}
+
+size_t JIT_OPERATION operationObjectIsFunction(ExecState* exec, JSGlobalObject* globalObject, JSCell* object)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ ASSERT(jsDynamicCast<JSObject*>(object));
+
+ if (object->structure(vm)->masqueradesAsUndefined(globalObject))
+ return false;
+ if (object->type() == JSFunctionType)
+ return true;
+ if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) {
+ CallData callData;
+ if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone)
+ return true;
+ }
+
+ return false;
+}
+
+JSCell* JIT_OPERATION operationTypeOfObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ ASSERT(jsDynamicCast<JSObject*>(object));
+
+ if (object->structure(vm)->masqueradesAsUndefined(globalObject))
+ return vm.smallStrings.undefinedString();
+ if (object->type() == JSFunctionType)
+ return vm.smallStrings.functionString();
+ if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) {
+ CallData callData;
+ if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone)
+ return vm.smallStrings.functionString();
+ }
+
+ return vm.smallStrings.objectString();
+}
+
+int32_t JIT_OPERATION operationTypeOfObjectAsTypeofType(ExecState* exec, JSGlobalObject* globalObject, JSCell* object)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ ASSERT(jsDynamicCast<JSObject*>(object));
+
+ if (object->structure(vm)->masqueradesAsUndefined(globalObject))
+ return static_cast<int32_t>(TypeofType::Undefined);
+ if (object->type() == JSFunctionType)
+ return static_cast<int32_t>(TypeofType::Function);
+ if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) {
+ CallData callData;
+ if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone)
+ return static_cast<int32_t>(TypeofType::Function);
+ }
+
+ return static_cast<int32_t>(TypeofType::Object);
}
char* JIT_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState* exec)
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 6b7441d..b966c8d 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -103,9 +103,10 @@
JSCell* JIT_OPERATION operationCreateClonedArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, int32_t argumentCount);
JSCell* JIT_OPERATION operationCreateClonedArguments(ExecState*, Structure*, Register* argumentStart, int32_t length, JSFunction* callee);
double JIT_OPERATION operationFModOnInts(int32_t, int32_t) WTF_INTERNAL;
-size_t JIT_OPERATION operationIsObjectOrNull(ExecState*, EncodedJSValue) WTF_INTERNAL;
-size_t JIT_OPERATION operationIsFunction(EncodedJSValue) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationTypeOf(ExecState*, JSCell*) WTF_INTERNAL;
+size_t JIT_OPERATION operationObjectIsObject(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
+size_t JIT_OPERATION operationObjectIsFunction(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationTypeOfObject(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
+int32_t JIT_OPERATION operationTypeOfObjectAsTypeofType(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
char* JIT_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState*) WTF_INTERNAL;
char* JIT_OPERATION operationAllocatePropertyStorage(ExecState*, size_t newSize) WTF_INTERNAL;
char* JIT_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState*, JSObject*) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 86e3d7c..e2a2369 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -4815,6 +4815,118 @@
return true;
}
+void SpeculativeJIT::compileIsObjectOrNull(Node* node)
+{
+ JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+
+ JSValueOperand value(this, node->child1());
+ JSValueRegs valueRegs = value.jsValueRegs();
+
+ GPRTemporary result(this);
+ GPRReg resultGPR = result.gpr();
+
+ JITCompiler::Jump isCell = m_jit.branchIfCell(valueRegs);
+
+ JITCompiler::Jump isNull = m_jit.branchIfEqual(valueRegs, jsNull());
+ JITCompiler::Jump isNonNullNonCell = m_jit.jump();
+
+ isCell.link(&m_jit);
+ JITCompiler::Jump isFunction = m_jit.branchIfFunction(valueRegs.payloadGPR());
+ JITCompiler::Jump notObject = m_jit.branchIfNotObject(valueRegs.payloadGPR());
+
+ JITCompiler::Jump slowPath = m_jit.branchTest8(
+ JITCompiler::NonZero,
+ JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoFlagsOffset()),
+ TrustedImm32(MasqueradesAsUndefined | TypeOfShouldCallGetCallData));
+
+ isNull.link(&m_jit);
+ m_jit.move(TrustedImm32(1), resultGPR);
+ JITCompiler::Jump done = m_jit.jump();
+
+ isNonNullNonCell.link(&m_jit);
+ isFunction.link(&m_jit);
+ notObject.link(&m_jit);
+ m_jit.move(TrustedImm32(0), resultGPR);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationObjectIsObject, resultGPR, globalObject,
+ valueRegs.payloadGPR()));
+
+ done.link(&m_jit);
+
+ unblessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileIsFunction(Node* node)
+{
+ JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+
+ JSValueOperand value(this, node->child1());
+ JSValueRegs valueRegs = value.jsValueRegs();
+
+ GPRTemporary result(this);
+ GPRReg resultGPR = result.gpr();
+
+ JITCompiler::Jump notCell = m_jit.branchIfNotCell(valueRegs);
+ JITCompiler::Jump isFunction = m_jit.branchIfFunction(valueRegs.payloadGPR());
+ JITCompiler::Jump notObject = m_jit.branchIfNotObject(valueRegs.payloadGPR());
+
+ JITCompiler::Jump slowPath = m_jit.branchTest8(
+ JITCompiler::NonZero,
+ JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoFlagsOffset()),
+ TrustedImm32(MasqueradesAsUndefined | TypeOfShouldCallGetCallData));
+
+ notCell.link(&m_jit);
+ notObject.link(&m_jit);
+ m_jit.move(TrustedImm32(0), resultGPR);
+ JITCompiler::Jump done = m_jit.jump();
+
+ isFunction.link(&m_jit);
+ m_jit.move(TrustedImm32(1), resultGPR);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationObjectIsFunction, resultGPR, globalObject,
+ valueRegs.payloadGPR()));
+
+ done.link(&m_jit);
+
+ unblessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileTypeOf(Node* node)
+{
+ JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+
+ JSValueOperand value(this, node->child1());
+ JSValueRegs valueRegs = value.jsValueRegs();
+
+ GPRTemporary result(this);
+ GPRReg resultGPR = result.gpr();
+
+ JITCompiler::JumpList done;
+ JITCompiler::Jump slowPath;
+ m_jit.emitTypeOf(
+ valueRegs, resultGPR,
+ [&] (TypeofType type, bool fallsThrough) {
+ m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.typeString(type)), resultGPR);
+ if (!fallsThrough)
+ done.append(m_jit.jump());
+ },
+ [&] (JITCompiler::Jump theSlowPath) {
+ slowPath = theSlowPath;
+ });
+ done.link(&m_jit);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationTypeOfObject, resultGPR, globalObject,
+ valueRegs.payloadGPR()));
+
+ cellResult(resultGPR, node);
+}
+
void SpeculativeJIT::compileAllocatePropertyStorage(Node* node)
{
if (node->transition()->previous->couldHaveIndexingHeader()) {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 9d72e89..67e673b 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -792,13 +792,7 @@
#if USE(JSVALUE64)
jsValueResult(reg, node, DataFormatJSBoolean, mode);
#else
- if (mode == CallUseChildren)
- useChildren(node);
-
- VirtualRegister virtualRegister = node->virtualRegister();
- m_gprs.retain(reg, virtualRegister, SpillOrderBoolean);
- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister);
- info.initBoolean(node, node->refCount(), reg);
+ booleanResult(reg, node, mode);
#endif
}
void unblessedBooleanResult(GPRReg reg, Node* node, UseChildrenMode mode = CallUseChildren)
@@ -1060,6 +1054,18 @@
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(S_JITOperation_EGC operation, GPRReg result, JSGlobalObject* globalObject, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(globalObject), arg2);
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
+
+ JITCompiler::Call callOperation(C_JITOperation_EGC operation, GPRReg result, JSGlobalObject* globalObject, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(globalObject), arg2);
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
+
JITCompiler::Call callOperation(Jss_JITOperation_EZ operation, GPRReg result, GPRReg arg1)
{
m_jit.setupArgumentsWithExecState(arg1);
@@ -2214,6 +2220,9 @@
void compileCreateClonedArguments(Node*);
void compileNotifyWrite(Node*);
bool compileRegExpExec(Node*);
+ void compileIsObjectOrNull(Node*);
+ void compileIsFunction(Node*);
+ void compileTypeOf(Node*);
void moveTrueTo(GPRReg);
void moveFalseTo(GPRReg);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index fd9f350..f9ca771 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4107,86 +4107,16 @@
}
case IsObjectOrNull: {
- JSValueOperand value(this, node->child1());
- GPRReg valueTagGPR = value.tagGPR();
- GPRReg valuePayloadGPR = value.payloadGPR();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- flushRegisters();
- callOperation(operationIsObjectOrNull, resultGPR, valueTagGPR, valuePayloadGPR);
- booleanResult(result.gpr(), node);
+ compileIsObjectOrNull(node);
break;
}
case IsFunction: {
- JSValueOperand value(this, node->child1());
- GPRReg valueTagGPR = value.tagGPR();
- GPRReg valuePayloadGPR = value.payloadGPR();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- flushRegisters();
- callOperation(operationIsFunction, resultGPR, valueTagGPR, valuePayloadGPR);
- booleanResult(result.gpr(), node);
+ compileIsFunction(node);
break;
}
case TypeOf: {
- JSValueOperand value(this, node->child1(), ManualOperandSpeculation);
- GPRReg tagGPR = value.tagGPR();
- GPRReg payloadGPR = value.payloadGPR();
- GPRTemporary temp(this);
- GPRReg tempGPR = temp.gpr();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- JITCompiler::JumpList doneJumps;
-
- flushRegisters();
-
- ASSERT(node->child1().useKind() == UntypedUse || node->child1().useKind() == CellUse || node->child1().useKind() == StringUse);
-
- JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(value.jsValueRegs());
- if (node->child1().useKind() != UntypedUse)
- DFG_TYPE_CHECK(JSValueRegs(tagGPR, payloadGPR), node->child1(), SpecCell, isNotCell);
-
- if (!node->child1()->shouldSpeculateObject() || node->child1().useKind() == StringUse) {
- JITCompiler::Jump notString = m_jit.branchIfNotString(payloadGPR);
- if (node->child1().useKind() == StringUse)
- DFG_TYPE_CHECK(JSValueRegs(tagGPR, payloadGPR), node->child1(), SpecString, notString);
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.stringString()), resultGPR);
- doneJumps.append(m_jit.jump());
- if (node->child1().useKind() != StringUse) {
- notString.link(&m_jit);
- callOperation(operationTypeOf, resultGPR, payloadGPR);
- doneJumps.append(m_jit.jump());
- }
- } else {
- callOperation(operationTypeOf, resultGPR, payloadGPR);
- doneJumps.append(m_jit.jump());
- }
-
- if (node->child1().useKind() == UntypedUse) {
- isNotCell.link(&m_jit);
-
- m_jit.add32(TrustedImm32(1), tagGPR, tempGPR);
- JITCompiler::Jump notNumber = m_jit.branch32(JITCompiler::AboveOrEqual, tempGPR, JITCompiler::TrustedImm32(JSValue::LowestTag + 1));
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.numberString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notNumber.link(&m_jit);
-
- JITCompiler::Jump notUndefined = m_jit.branch32(JITCompiler::NotEqual, tagGPR, TrustedImm32(JSValue::UndefinedTag));
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.undefinedString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notUndefined.link(&m_jit);
-
- JITCompiler::Jump notNull = m_jit.branch32(JITCompiler::NotEqual, tagGPR, TrustedImm32(JSValue::NullTag));
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.objectString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notNull.link(&m_jit);
-
- // Only boolean left
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.booleanString()), resultGPR);
- }
- doneJumps.link(&m_jit);
- cellResult(resultGPR, node);
+ compileTypeOf(node);
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 1d0857f..35c41e4 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4148,82 +4148,17 @@
}
case IsObjectOrNull: {
- JSValueOperand value(this, node->child1());
- GPRReg valueGPR = value.gpr();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- flushRegisters();
- callOperation(operationIsObjectOrNull, resultGPR, valueGPR);
- m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
- jsValueResult(result.gpr(), node, DataFormatJSBoolean);
+ compileIsObjectOrNull(node);
break;
}
case IsFunction: {
- JSValueOperand value(this, node->child1());
- GPRReg valueGPR = value.gpr();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- flushRegisters();
- callOperation(operationIsFunction, resultGPR, valueGPR);
- m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
- jsValueResult(result.gpr(), node, DataFormatJSBoolean);
+ compileIsFunction(node);
break;
}
case TypeOf: {
- JSValueOperand value(this, node->child1(), ManualOperandSpeculation);
- GPRReg valueGPR = value.gpr();
- GPRFlushedCallResult result(this);
- GPRReg resultGPR = result.gpr();
- JITCompiler::JumpList doneJumps;
-
- flushRegisters();
-
- ASSERT(node->child1().useKind() == UntypedUse || node->child1().useKind() == CellUse || node->child1().useKind() == StringUse);
-
- JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(JSValueRegs(valueGPR));
- if (node->child1().useKind() != UntypedUse)
- DFG_TYPE_CHECK(JSValueSource(valueGPR), node->child1(), SpecCell, isNotCell);
-
- if (!node->child1()->shouldSpeculateObject() || node->child1().useKind() == StringUse) {
- JITCompiler::Jump notString = m_jit.branchIfNotString(valueGPR);
- if (node->child1().useKind() == StringUse)
- DFG_TYPE_CHECK(JSValueSource(valueGPR), node->child1(), SpecString, notString);
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.stringString()), resultGPR);
- doneJumps.append(m_jit.jump());
- if (node->child1().useKind() != StringUse) {
- notString.link(&m_jit);
- callOperation(operationTypeOf, resultGPR, valueGPR);
- doneJumps.append(m_jit.jump());
- }
- } else {
- callOperation(operationTypeOf, resultGPR, valueGPR);
- doneJumps.append(m_jit.jump());
- }
-
- if (node->child1().useKind() == UntypedUse) {
- isNotCell.link(&m_jit);
- JITCompiler::Jump notNumber = m_jit.branchTest64(JITCompiler::Zero, valueGPR, GPRInfo::tagTypeNumberRegister);
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.numberString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notNumber.link(&m_jit);
-
- JITCompiler::Jump notUndefined = m_jit.branch64(JITCompiler::NotEqual, valueGPR, JITCompiler::TrustedImm64(ValueUndefined));
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.undefinedString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notUndefined.link(&m_jit);
-
- JITCompiler::Jump notNull = m_jit.branch64(JITCompiler::NotEqual, valueGPR, JITCompiler::TrustedImm64(ValueNull));
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.objectString()), resultGPR);
- doneJumps.append(m_jit.jump());
- notNull.link(&m_jit);
-
- // Only boolean left
- m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.booleanString()), resultGPR);
- }
- doneJumps.link(&m_jit);
- cellResult(resultGPR, node);
+ compileTypeOf(node);
break;
}
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 60dcf79..7a8db3c 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -189,6 +189,7 @@
case GetMyArgumentByVal:
case ForwardVarargs:
case Switch:
+ case TypeOf:
// These are OK.
break;
case Identity:
diff --git a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
index 097b71c..3d66eca 100644
--- a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
+++ b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
@@ -60,6 +60,7 @@
macro(C_JITOperation_EC, functionType(intPtr, intPtr, intPtr)) \
macro(C_JITOperation_ECZ, functionType(intPtr, intPtr, intPtr, int32)) \
macro(C_JITOperation_ECZC, functionType(intPtr, intPtr, intPtr, int32, intPtr)) \
+ macro(C_JITOperation_EGC, functionType(intPtr, intPtr, intPtr, intPtr)) \
macro(C_JITOperation_EJ, functionType(intPtr, intPtr, int64)) \
macro(C_JITOperation_EJssJss, functionType(intPtr, intPtr, intPtr, intPtr)) \
macro(C_JITOperation_EJssJssJss, functionType(intPtr, intPtr, intPtr, intPtr, intPtr)) \
@@ -97,6 +98,7 @@
macro(P_JITOperation_EStZ, functionType(intPtr, intPtr, intPtr, int32)) \
macro(Q_JITOperation_D, functionType(int64, doubleType)) \
macro(Q_JITOperation_J, functionType(int64, int64)) \
+ macro(S_JITOperation_EGC, functionType(intPtr, intPtr, intPtr, intPtr)) \
macro(S_JITOperation_EJ, functionType(intPtr, intPtr, int64)) \
macro(S_JITOperation_EJJ, functionType(intPtr, intPtr, int64, int64)) \
macro(S_JITOperation_J, functionType(intPtr, int64)) \
@@ -112,6 +114,7 @@
macro(V_JITOperation_Z, functionType(voidType, int32)) \
macro(Z_JITOperation_D, functionType(int32, doubleType)) \
macro(Z_JITOperation_EC, functionType(int32, intPtr, intPtr)) \
+ macro(Z_JITOperation_EGC, functionType(int32, intPtr, intPtr, intPtr)) \
macro(Z_JITOperation_EJZ, functionType(int32, intPtr, int64, int32)) \
macro(Z_JITOperation_ESJss, functionType(int32, intPtr, intPtr, int64)) \
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 8b9aa34..32f3ee0 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -796,6 +796,9 @@
case IsFunction:
compileIsFunction();
break;
+ case TypeOf:
+ compileTypeOf();
+ break;
case CheckHasInstance:
compileCheckHasInstance();
break;
@@ -4623,16 +4626,117 @@
void compileIsObjectOrNull()
{
- LValue pointerResult = vmCall(
- m_out.operation(operationIsObjectOrNull), m_callFrame, lowJSValue(m_node->child1()));
- setBoolean(m_out.notNull(pointerResult));
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+ Edge child = m_node->child1();
+ LValue value = lowJSValue(child);
+
+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull cell case"));
+ LBasicBlock notFunctionCase = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull not function case"));
+ LBasicBlock objectCase = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull object case"));
+ LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull slow path"));
+ LBasicBlock notCellCase = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull not cell case"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("IsObjectOrNull continuation"));
+
+ m_out.branch(isCell(value, provenType(child)), unsure(cellCase), unsure(notCellCase));
+
+ LBasicBlock lastNext = m_out.appendTo(cellCase, notFunctionCase);
+ ValueFromBlock isFunctionResult = m_out.anchor(m_out.booleanFalse);
+ m_out.branch(
+ isFunction(value, provenType(child)),
+ unsure(continuation), unsure(notFunctionCase));
+
+ m_out.appendTo(notFunctionCase, objectCase);
+ ValueFromBlock notObjectResult = m_out.anchor(m_out.booleanFalse);
+ m_out.branch(
+ isObject(value, provenType(child)),
+ unsure(objectCase), unsure(continuation));
+
+ m_out.appendTo(objectCase, slowPath);
+ ValueFromBlock objectResult = m_out.anchor(m_out.booleanTrue);
+ m_out.branch(
+ isExoticForTypeof(value, provenType(child)),
+ rarely(slowPath), usually(continuation));
+
+ m_out.appendTo(slowPath, notCellCase);
+ LValue slowResultValue = vmCall(
+ m_out.operation(operationObjectIsObject), m_callFrame, weakPointer(globalObject),
+ value);
+ ValueFromBlock slowResult = m_out.anchor(m_out.notNull(slowResultValue));
+ m_out.jump(continuation);
+
+ m_out.appendTo(notCellCase, continuation);
+ LValue notCellResultValue = m_out.equal(value, m_out.constInt64(JSValue::encode(jsNull())));
+ ValueFromBlock notCellResult = m_out.anchor(notCellResultValue);
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ LValue result = m_out.phi(
+ m_out.boolean,
+ isFunctionResult, notObjectResult, objectResult, slowResult, notCellResult);
+ setBoolean(result);
}
void compileIsFunction()
{
- LValue pointerResult = vmCall(
- m_out.operation(operationIsFunction), lowJSValue(m_node->child1()));
- setBoolean(m_out.notNull(pointerResult));
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+ Edge child = m_node->child1();
+ LValue value = lowJSValue(child);
+
+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("IsFunction cell case"));
+ LBasicBlock notFunctionCase = FTL_NEW_BLOCK(m_out, ("IsFunction not function case"));
+ LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("IsFunction slow path"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("IsFunction continuation"));
+
+ ValueFromBlock notCellResult = m_out.anchor(m_out.booleanFalse);
+ m_out.branch(
+ isCell(value, provenType(child)), unsure(cellCase), unsure(continuation));
+
+ LBasicBlock lastNext = m_out.appendTo(cellCase, notFunctionCase);
+ ValueFromBlock functionResult = m_out.anchor(m_out.booleanTrue);
+ m_out.branch(
+ isFunction(value, provenType(child)),
+ unsure(continuation), unsure(notFunctionCase));
+
+ m_out.appendTo(notFunctionCase, slowPath);
+ ValueFromBlock objectResult = m_out.anchor(m_out.booleanFalse);
+ m_out.branch(
+ isExoticForTypeof(value, provenType(child)),
+ rarely(slowPath), usually(continuation));
+
+ m_out.appendTo(slowPath, continuation);
+ LValue slowResultValue = vmCall(
+ m_out.operation(operationObjectIsFunction), m_callFrame, weakPointer(globalObject),
+ value);
+ ValueFromBlock slowResult = m_out.anchor(m_out.notNull(slowResultValue));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ LValue result = m_out.phi(
+ m_out.boolean, notCellResult, functionResult, objectResult, slowResult);
+ setBoolean(result);
+ }
+
+ void compileTypeOf()
+ {
+ Edge child = m_node->child1();
+ LValue value = lowJSValue(child);
+
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("TypeOf continuation"));
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(continuation);
+
+ Vector<ValueFromBlock> results;
+
+ buildTypeOf(
+ child, value,
+ [&] (TypeofType type) {
+ results.append(m_out.anchor(weakPointer(vm().smallStrings.typeString(type))));
+ m_out.jump(continuation);
+ });
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(m_out.int64, results));
}
void compileIn()
@@ -6356,6 +6460,143 @@
Weight(data->fallThrough.count));
}
+ // Calls the functor at the point of code generation where we know what the result type is.
+ // You can emit whatever code you like at that point. Expects you to terminate the basic block.
+ // When buildTypeOf() returns, it will have terminated all basic blocks that it created. So, if
+ // you aren't using this as the terminator of a high-level block, you should create your own
+ // contination and set it as the nextBlock (m_out.insertNewBlocksBefore(continuation)) before
+ // calling this. For example:
+ //
+ // LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("My continuation"));
+ // LBasicBlock lastNext = m_out.insertNewBlocksBefore(continuation);
+ // buildTypeOf(
+ // child, value,
+ // [&] (TypeofType type) {
+ // do things;
+ // m_out.jump(continuation);
+ // });
+ // m_out.appendTo(continuation, lastNext);
+ template<typename Functor>
+ void buildTypeOf(Edge child, LValue value, const Functor& functor)
+ {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+ // Implements the following branching structure:
+ //
+ // if (is cell) {
+ // if (is object) {
+ // if (is function) {
+ // return function;
+ // } else if (doesn't have call trap and doesn't masquerade as undefined) {
+ // return object
+ // } else {
+ // return slowPath();
+ // }
+ // } else if (is string) {
+ // return string
+ // } else {
+ // return symbol
+ // }
+ // } else if (is number) {
+ // return number
+ // } else if (is null) {
+ // return object
+ // } else if (is boolean) {
+ // return boolean
+ // } else {
+ // return undefined
+ // }
+
+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf cell case"));
+ LBasicBlock objectCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf object case"));
+ LBasicBlock functionCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf function case"));
+ LBasicBlock notFunctionCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf not function case"));
+ LBasicBlock reallyObjectCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf really object case"));
+ LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("buildTypeOf slow path"));
+ LBasicBlock unreachable = FTL_NEW_BLOCK(m_out, ("buildTypeOf unreachable"));
+ LBasicBlock notObjectCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf not object case"));
+ LBasicBlock stringCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf string case"));
+ LBasicBlock symbolCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf symbol case"));
+ LBasicBlock notCellCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf not cell case"));
+ LBasicBlock numberCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf number case"));
+ LBasicBlock notNumberCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf not number case"));
+ LBasicBlock notNullCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf not null case"));
+ LBasicBlock booleanCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf boolean case"));
+ LBasicBlock undefinedCase = FTL_NEW_BLOCK(m_out, ("buildTypeOf undefined case"));
+
+ m_out.branch(isCell(value, provenType(child)), unsure(cellCase), unsure(notCellCase));
+
+ LBasicBlock lastNext = m_out.appendTo(cellCase, objectCase);
+ m_out.branch(isObject(value, provenType(child)), unsure(objectCase), unsure(notObjectCase));
+
+ m_out.appendTo(objectCase, functionCase);
+ m_out.branch(
+ isFunction(value, provenType(child) & SpecObject),
+ unsure(functionCase), unsure(notFunctionCase));
+
+ m_out.appendTo(functionCase, notFunctionCase);
+ functor(TypeofType::Function);
+
+ m_out.appendTo(notFunctionCase, reallyObjectCase);
+ m_out.branch(
+ isExoticForTypeof(value, provenType(child) & (SpecObject - SpecFunction)),
+ rarely(slowPath), usually(reallyObjectCase));
+
+ m_out.appendTo(reallyObjectCase, slowPath);
+ functor(TypeofType::Object);
+
+ m_out.appendTo(slowPath, unreachable);
+ LValue result = vmCall(
+ m_out.operation(operationTypeOfObjectAsTypeofType), m_callFrame,
+ weakPointer(globalObject), value);
+ Vector<SwitchCase, 3> cases;
+ cases.append(SwitchCase(m_out.constInt32(static_cast<int32_t>(TypeofType::Undefined)), undefinedCase));
+ cases.append(SwitchCase(m_out.constInt32(static_cast<int32_t>(TypeofType::Object)), reallyObjectCase));
+ cases.append(SwitchCase(m_out.constInt32(static_cast<int32_t>(TypeofType::Function)), functionCase));
+ m_out.switchInstruction(result, cases, unreachable, Weight());
+
+ m_out.appendTo(unreachable, notObjectCase);
+ m_out.unreachable();
+
+ m_out.appendTo(notObjectCase, stringCase);
+ m_out.branch(
+ isString(value, provenType(child) & (SpecCell - SpecObject)),
+ unsure(stringCase), unsure(symbolCase));
+
+ m_out.appendTo(stringCase, symbolCase);
+ functor(TypeofType::String);
+
+ m_out.appendTo(symbolCase, notCellCase);
+ functor(TypeofType::Symbol);
+
+ m_out.appendTo(notCellCase, numberCase);
+ m_out.branch(
+ isNumber(value, provenType(child) & ~SpecCell),
+ unsure(numberCase), unsure(notNumberCase));
+
+ m_out.appendTo(numberCase, notNumberCase);
+ functor(TypeofType::Number);
+
+ m_out.appendTo(notNumberCase, notNullCase);
+ LValue isNull;
+ if (provenType(child) & SpecOther)
+ isNull = m_out.equal(value, m_out.constInt64(ValueNull));
+ else
+ isNull = m_out.booleanFalse;
+ m_out.branch(isNull, unsure(reallyObjectCase), unsure(notNullCase));
+
+ m_out.appendTo(notNullCase, booleanCase);
+ m_out.branch(
+ isBoolean(value, provenType(child) & ~(SpecCell | SpecFullNumber)),
+ unsure(booleanCase), unsure(undefinedCase));
+
+ m_out.appendTo(booleanCase, undefinedCase);
+ functor(TypeofType::Boolean);
+
+ m_out.appendTo(undefinedCase, lastNext);
+ functor(TypeofType::Undefined);
+ }
+
LValue doubleToInt32(LValue doubleValue, double low, double high, bool isSigned = true)
{
LBasicBlock greatEnough = FTL_NEW_BLOCK(m_out, ("doubleToInt32 greatEnough"));
@@ -7177,6 +7418,15 @@
return proven;
return isNotType(cell, JSFunctionType);
}
+
+ LValue isExoticForTypeof(LValue cell, SpeculatedType type = SpecFullTop)
+ {
+ if (!(type & SpecObjectOther))
+ return m_out.booleanFalse;
+ return m_out.testNonZero8(
+ m_out.load8(cell, m_heaps.JSCell_typeInfoFlags),
+ m_out.constInt8(MasqueradesAsUndefined | TypeOfShouldCallGetCallData));
+ }
LValue isType(LValue cell, JSType type)
{
diff --git a/Source/JavaScriptCore/ftl/FTLSwitchCase.h b/Source/JavaScriptCore/ftl/FTLSwitchCase.h
index 4eb8cba..cd6bc07 100644
--- a/Source/JavaScriptCore/ftl/FTLSwitchCase.h
+++ b/Source/JavaScriptCore/ftl/FTLSwitchCase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -41,7 +41,7 @@
{
}
- SwitchCase(LValue value, LBasicBlock target, Weight weight)
+ SwitchCase(LValue value, LBasicBlock target, Weight weight = Weight())
: m_value(value)
, m_target(target)
, m_weight(weight)
diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.h b/Source/JavaScriptCore/jit/AssemblyHelpers.h
index 0f88a9f..1f0f587 100644
--- a/Source/JavaScriptCore/jit/AssemblyHelpers.h
+++ b/Source/JavaScriptCore/jit/AssemblyHelpers.h
@@ -33,6 +33,7 @@
#include "GPRInfo.h"
#include "JITCode.h"
#include "MacroAssembler.h"
+#include "TypeofType.h"
#include "VM.h"
namespace JSC {
@@ -367,6 +368,36 @@
storePtr(tag, Address(stackPointerRegister, entry * static_cast<ptrdiff_t>(sizeof(Register)) - prologueStackPointerDelta() + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
}
#endif
+
+ JumpList branchIfNotEqual(JSValueRegs regs, JSValue value)
+ {
+#if USE(JSVALUE64)
+ return branch64(NotEqual, regs.gpr(), TrustedImm64(JSValue::encode(value)));
+#else
+ JumpList result;
+ result.append(branch32(NotEqual, regs.tagGPR(), TrustedImm32(value.tag())));
+ if (value.isEmpty() || value.isUndefinedOrNull())
+ return result; // These don't have anything interesting in the payload.
+ result.append(branch32(NotEqual, regs.payloadGPR(), TrustedImm32(value.payload())));
+ return result;
+#endif
+ }
+
+ Jump branchIfEqual(JSValueRegs regs, JSValue value)
+ {
+#if USE(JSVALUE64)
+ return branch64(Equal, regs.gpr(), TrustedImm64(JSValue::encode(value)));
+#else
+ Jump notEqual;
+ // These don't have anything interesting in the payload.
+ if (!value.isEmpty() && !value.isUndefinedOrNull())
+ notEqual = branch32(NotEqual, regs.payloadGPR(), TrustedImm32(value.payload()));
+ Jump result = branch32(Equal, regs.tagGPR(), TrustedImm32(value.tag()));
+ if (notEqual.isSet())
+ notEqual.link(this);
+ return result;
+#endif
+ }
Jump branchIfNotCell(GPRReg reg)
{
@@ -426,6 +457,56 @@
#endif
}
+ // Note that the tempGPR is not used in 64-bit mode.
+ Jump branchIfNumber(JSValueRegs regs, GPRReg tempGPR)
+ {
+#if USE(JSVALUE64)
+ UNUSED_PARAM(tempGPR);
+ return branchTest64(NonZero, regs.gpr(), GPRInfo::tagTypeNumberRegister);
+#else
+ add32(TrustedImm32(1), regs.tagGPR(), tempGPR);
+ return branch32(Below, tempGPR, TrustedImm32(JSValue::LowestTag + 1));
+#endif
+ }
+
+ // Note that the tempGPR is not used in 64-bit mode.
+ Jump branchIfNotNumber(JSValueRegs regs, GPRReg tempGPR)
+ {
+#if USE(JSVALUE64)
+ UNUSED_PARAM(tempGPR);
+ return branchTest64(Zero, regs.gpr(), GPRInfo::tagTypeNumberRegister);
+#else
+ add32(TrustedImm32(1), regs.tagGPR(), tempGPR);
+ return branch32(AboveOrEqual, tempGPR, TrustedImm32(JSValue::LowestTag + 1));
+#endif
+ }
+
+ // Note that the tempGPR is not used in 32-bit mode.
+ Jump branchIfBoolean(JSValueRegs regs, GPRReg tempGPR)
+ {
+#if USE(JSVALUE64)
+ move(regs.gpr(), tempGPR);
+ xor64(TrustedImm32(static_cast<int32_t>(ValueFalse)), tempGPR);
+ return branchTest64(Zero, tempGPR, TrustedImm32(static_cast<int32_t>(~1)));
+#else
+ UNUSED_PARAM(tempGPR);
+ return branch32(Equal, regs.tagGPR(), TrustedImm32(JSValue::BooleanTag));
+#endif
+ }
+
+ // Note that the tempGPR is not used in 32-bit mode.
+ Jump branchIfNotBoolean(JSValueRegs regs, GPRReg tempGPR)
+ {
+#if USE(JSVALUE64)
+ move(regs.gpr(), tempGPR);
+ xor64(TrustedImm32(static_cast<int32_t>(ValueFalse)), tempGPR);
+ return branchTest64(NonZero, tempGPR, TrustedImm32(static_cast<int32_t>(~1)));
+#else
+ UNUSED_PARAM(tempGPR);
+ return branch32(NotEqual, regs.tagGPR(), TrustedImm32(JSValue::BooleanTag));
+#endif
+ }
+
Jump branchIfObject(GPRReg cellGPR)
{
return branch8(
@@ -707,6 +788,23 @@
}
#endif
+ void boxBooleanPayload(GPRReg boolGPR, GPRReg payloadGPR)
+ {
+#if USE(JSVALUE64)
+ add32(TrustedImm32(ValueFalse), boolGPR, payloadGPR);
+#else
+ move(boolGPR, payloadGPR);
+#endif
+ }
+
+ void boxBoolean(GPRReg boolGPR, JSValueRegs boxedRegs)
+ {
+ boxBooleanPayload(boolGPR, boxedRegs.payloadGPR());
+#if USE(JSVALUE32_64)
+ move(TrustedImm32(JSValue::BooleanTag), boxedRegs.tagGPR());
+#endif
+ }
+
void callExceptionFuzz();
enum ExceptionCheckKind { NormalExceptionCheck, InvertedExceptionCheck };
@@ -843,6 +941,82 @@
uint8_t* address = reinterpret_cast<uint8_t*>(cell) + JSCell::gcDataOffset();
return branchTest8(MacroAssembler::NonZero, MacroAssembler::AbsoluteAddress(address));
}
+
+ // Emits the branch structure for typeof. The code emitted by this doesn't fall through. The
+ // functor is called at those points where we have pinpointed a type. One way to use this is to
+ // have the functor emit the code to put the type string into an appropriate register and then
+ // jump out. A secondary functor is used for the call trap and masquerades-as-undefined slow
+ // case. It is passed the unlinked jump to the slow case.
+ template<typename Functor, typename SlowPathFunctor>
+ void emitTypeOf(
+ JSValueRegs regs, GPRReg tempGPR, const Functor& functor,
+ const SlowPathFunctor& slowPathFunctor)
+ {
+ // Implements the following branching structure:
+ //
+ // if (is cell) {
+ // if (is object) {
+ // if (is function) {
+ // return function;
+ // } else if (doesn't have call trap and doesn't masquerade as undefined) {
+ // return object
+ // } else {
+ // return slowPath();
+ // }
+ // } else if (is string) {
+ // return string
+ // } else {
+ // return symbol
+ // }
+ // } else if (is number) {
+ // return number
+ // } else if (is null) {
+ // return object
+ // } else if (is boolean) {
+ // return boolean
+ // } else {
+ // return undefined
+ // }
+
+ Jump notCell = branchIfNotCell(regs);
+
+ GPRReg cellGPR = regs.payloadGPR();
+ Jump notObject = branchIfNotObject(cellGPR);
+
+ Jump notFunction = branchIfNotFunction(cellGPR);
+ functor(TypeofType::Function, false);
+
+ notFunction.link(this);
+ slowPathFunctor(
+ branchTest8(
+ NonZero,
+ Address(cellGPR, JSCell::typeInfoFlagsOffset()),
+ TrustedImm32(MasqueradesAsUndefined | TypeOfShouldCallGetCallData)));
+ functor(TypeofType::Object, false);
+
+ notObject.link(this);
+
+ Jump notString = branchIfNotString(cellGPR);
+ functor(TypeofType::String, false);
+ notString.link(this);
+ functor(TypeofType::Symbol, false);
+
+ notCell.link(this);
+
+ Jump notNumber = branchIfNotNumber(regs, tempGPR);
+ functor(TypeofType::Number, false);
+ notNumber.link(this);
+
+ JumpList notNull = branchIfNotEqual(regs, jsNull());
+ functor(TypeofType::Object, false);
+ notNull.link(this);
+
+ Jump notBoolean = branchIfNotBoolean(regs, tempGPR);
+ functor(TypeofType::Boolean, false);
+ notBoolean.link(this);
+
+ functor(TypeofType::Undefined, true);
+ }
Vector<BytecodeAndMachineOffset>& decodedCodeMapFor(CodeBlock*);
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index c65f874..4c81d85 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -63,6 +63,7 @@
D: double
E: ExecState*
F: CallFrame*
+ G: JSGlobalObject*
I: StringImpl*
Icf: InlineCallFrame*
Idc: const Identifier*
@@ -127,6 +128,7 @@
typedef JSCell* JIT_OPERATION (*C_JITOperation_ECZ)(ExecState*, JSCell*, int32_t);
typedef JSCell* JIT_OPERATION (*C_JITOperation_ECZC)(ExecState*, JSCell*, int32_t, JSCell*);
typedef JSCell* JIT_OPERATION (*C_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
+typedef JSCell* JIT_OPERATION (*C_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
typedef JSCell* JIT_OPERATION (*C_JITOperation_EIcf)(ExecState*, InlineCallFrame*);
typedef JSCell* JIT_OPERATION (*C_JITOperation_EJ)(ExecState*, EncodedJSValue);
typedef JSCell* JIT_OPERATION (*C_JITOperation_EJsc)(ExecState*, JSScope*);
@@ -157,10 +159,12 @@
typedef int32_t JIT_OPERATION (*Z_JITOperation_D)(double);
typedef int32_t JIT_OPERATION (*Z_JITOperation_E)(ExecState*);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EC)(ExecState*, JSCell*);
+typedef int32_t JIT_OPERATION (*Z_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
typedef int32_t JIT_OPERATION (*Z_JITOperation_ESJss)(ExecState*, size_t, JSString*);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EJZZ)(ExecState*, EncodedJSValue, int32_t, int32_t);
typedef size_t JIT_OPERATION (*S_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
+typedef size_t JIT_OPERATION (*S_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
typedef size_t JIT_OPERATION (*S_JITOperation_EJ)(ExecState*, EncodedJSValue);
typedef size_t JIT_OPERATION (*S_JITOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
typedef size_t JIT_OPERATION (*S_JITOperation_EOJss)(ExecState*, JSObject*, JSString*);
diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h
index fd2b76a..909bae1 100644
--- a/Source/JavaScriptCore/runtime/SmallStrings.h
+++ b/Source/JavaScriptCore/runtime/SmallStrings.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2009, 2015 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
#ifndef SmallStrings_h
#define SmallStrings_h
+#include "TypeofType.h"
#include "WriteBarrier.h"
#include <wtf/Noncopyable.h>
@@ -85,6 +86,29 @@
}
JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ACCESSOR_DEFINITION)
#undef JSC_COMMON_STRINGS_ACCESSOR_DEFINITION
+
+ JSString* typeString(TypeofType type) const
+ {
+ switch (type) {
+ case TypeofType::Undefined:
+ return undefinedString();
+ case TypeofType::Boolean:
+ return booleanString();
+ case TypeofType::Number:
+ return numberString();
+ case TypeofType::String:
+ return stringString();
+ case TypeofType::Symbol:
+ return symbolString();
+ case TypeofType::Object:
+ return objectString();
+ case TypeofType::Function:
+ return functionString();
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+ }
JSString* nullObjectString() const { return m_nullObjectString; }
JSString* undefinedObjectString() const { return m_undefinedObjectString; }
diff --git a/Source/JavaScriptCore/runtime/TypeofType.cpp b/Source/JavaScriptCore/runtime/TypeofType.cpp
new file mode 100644
index 0000000..db162b7
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/TypeofType.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#include "config.h"
+#include "TypeofType.h"
+
+namespace WTF {
+
+using namespace JSC;
+
+void printInternal(PrintStream& out, TypeofType type)
+{
+ switch (type) {
+ case TypeofType::Undefined:
+ out.print("undefined");
+ return;
+ case TypeofType::Boolean:
+ out.print("boolean");
+ return;
+ case TypeofType::Number:
+ out.print("number");
+ return;
+ case TypeofType::String:
+ out.print("string");
+ return;
+ case TypeofType::Symbol:
+ out.print("symbol");
+ return;
+ case TypeofType::Object:
+ out.print("object");
+ return;
+ case TypeofType::Function:
+ out.print("function");
+ return;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/runtime/TypeofType.h b/Source/JavaScriptCore/runtime/TypeofType.h
new file mode 100644
index 0000000..3eb78d4
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/TypeofType.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#ifndef TypeofType_h
+#define TypeofType_h
+
+#include <wtf/PrintStream.h>
+
+namespace JSC {
+
+enum class TypeofType {
+ Undefined,
+ Boolean,
+ Number,
+ String,
+ Symbol,
+ Object,
+ Function
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::TypeofType);
+
+} // namespace WTF
+
+#endif // TypeofType_h
+
diff --git a/Source/JavaScriptCore/tests/stress/type-of-functions-and-objects.js b/Source/JavaScriptCore/tests/stress/type-of-functions-and-objects.js
index ad539fe..9363158 100644
--- a/Source/JavaScriptCore/tests/stress/type-of-functions-and-objects.js
+++ b/Source/JavaScriptCore/tests/stress/type-of-functions-and-objects.js
@@ -26,48 +26,61 @@
noInline(baz);
noInline(fuzz);
-function expect(f, v, expected) {
- var result = f(v);
- if (result != expected)
- throw "Error: " + f.name + "(" + v + ") returned " + result + " instead of " + expected;
-}
+function test() {
+ var errors = [];
-function test(v, expected) {
- switch (expected) {
- case "function":
- expect(foo, v, "function");
- expect(bar, v, 2);
- expect(baz, v, true);
- expect(fuzz, v, false);
- break;
- case "object":
- expect(foo, v, "object");
- expect(bar, v, 1);
- expect(baz, v, false);
- expect(fuzz, v, true);
- break;
- case "other":
- var result = foo(v);
- if (result == "object" || result == "function")
- throw "Error: foo(" + v + ") returned " + result + " but expected something other than object or function";
- expect(bar, v, 3);
- expect(baz, v, false);
- expect(fuzz, v, false);
- break;
- default:
- throw "Bad expected case";
+ function testValue(v, expected) {
+ function expect(f, expected) {
+ var result = f(v);
+ if (result != expected)
+ errors.push(f.name + "(" + v + ") returned " + result + " instead of " + expected);
+ }
+
+ switch (expected) {
+ case "function":
+ expect(foo, "function");
+ expect(bar, 2);
+ expect(baz, true);
+ expect(fuzz, false);
+ break;
+ case "object":
+ expect(foo, "object");
+ expect(bar, 1);
+ expect(baz, false);
+ expect(fuzz, true);
+ break;
+ case "other":
+ var result = foo(v);
+ if (result == "object" || result == "function")
+ errors.push("foo(" + v + ") returned " + result + " but expected something other than object or function");
+ expect(bar, 3);
+ expect(baz, false);
+ expect(fuzz, false);
+ break;
+ default:
+ throw "Bad expected case";
+ }
}
+
+ testValue({}, "object");
+ testValue(function() { }, "function");
+ testValue("hello", "other");
+ testValue(42, "other");
+ testValue(null, "object");
+ testValue(void 0, "other");
+ testValue(42.5, "other");
+ testValue(Map, "function");
+ testValue(Date, "function");
+ testValue(Map.prototype, "object");
+
+ if (!errors.length)
+ return;
+
+ for (var i = 0; i < errors.length; ++i)
+ print("Error: " + errors[i]);
+ throw "Encountered errors during test run.";
}
-for (var i = 0; i < 10000; ++i) {
- test({}, "object");
- test(function() { }, "function");
- test("hello", "other");
- test(42, "other");
- test(null, "object");
- test(void 0, "other");
- test(42.5, "other");
- test(Map, "function");
- test(Date, "function");
- test(Map.prototype, "object");
-}
+for (var i = 0; i < 10000; ++i)
+ test();
+