Constructor returning null should construct an object instead of null
https://bugs.webkit.org/show_bug.cgi?id=141640
Reviewed by Filip Pizlo.
Source/JavaScriptCore:
When constructor code doesn't return object, constructor should return `this` object instead.
Since we used `op_is_object` for this check and `op_is_object` is intended to be used for `typeof`,
it allows `null` as an object.
This patch fixes it by introducing an new bytecode `op_is_object_or_null` for `typeof` use cases.
Instead, constructor uses simplified `is_object`.
As a result, `op_is_object` becomes fairly simple. So we introduce optimization for `op_is_object`.
1. LLInt and baseline JIT support `op_is_object` as a fast path.
2. DFG abstract interpreter support `op_is_object`. And recognize its speculated type and read-write effects.
3. DFG introduces inlined asm for `op_is_object` rather than calling a C++ function.
4. FTL lowers DFG's IsObject into LLVM IR.
And at the same time, this patch fixes isString / isObject predicate used for `op_is_object` and others
in LLInt, JIT, DFG and FTL.
Before introducing ES6 Symbol, JSCell is only used for object and string in user observable area.
So in many places, when the cell is not object, we recognize it as a string, and vice versa.
However, now ES6 Symbol is implemented as a JSCell, this assumption is broken.
So this patch stop using !isString as isObject.
To check whether a cell is an object, instead of seeing that structure ID of a cell is not stringStructure,
we examine typeInfo in JSCell.
* JavaScriptCore.order:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFor):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitEqualityOp):
(JSC::BytecodeGenerator::emitReturn):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
IsObject operation only touches JSCell typeInfoType.
And this value would be changed through structure transition.
As a result, IsObject can report that it doesn't read any information.
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
Just like IsString, IsObject is also fixed up.
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectEquality):
(JSC::DFG::SpeculativeJIT::compileStringToUntypedEquality):
(JSC::DFG::SpeculativeJIT::compileStringIdentToNotStringVarEquality):
(JSC::DFG::SpeculativeJIT::compileToStringOnCell):
(JSC::DFG::SpeculativeJIT::speculateObject):
(JSC::DFG::SpeculativeJIT::speculateObjectOrOther):
(JSC::DFG::SpeculativeJIT::speculateString):
(JSC::DFG::SpeculativeJIT::speculateNotStringVar):
(JSC::DFG::SpeculativeJIT::emitSwitchChar):
(JSC::DFG::SpeculativeJIT::emitSwitchString):
(JSC::DFG::SpeculativeJIT::branchIsObject):
(JSC::DFG::SpeculativeJIT::branchNotObject):
(JSC::DFG::SpeculativeJIT::branchIsString):
(JSC::DFG::SpeculativeJIT::branchNotString):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectEquality):
(JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):
(JSC::DFG::SpeculativeJIT::compileObjectOrOtherLogicalNot):
(JSC::DFG::SpeculativeJIT::emitObjectOrOtherBranch):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectEquality):
(JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):
(JSC::DFG::SpeculativeJIT::compileObjectOrOtherLogicalNot):
(JSC::DFG::SpeculativeJIT::emitObjectOrOtherBranch):
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileToString):
(JSC::FTL::LowerDFGToLLVM::compileIsObject):
(JSC::FTL::LowerDFGToLLVM::compileIsObjectOrNull):
(JSC::FTL::LowerDFGToLLVM::speculateTruthyObject):
(JSC::FTL::LowerDFGToLLVM::equalNullOrUndefined):
(JSC::FTL::LowerDFGToLLVM::isObject):
(JSC::FTL::LowerDFGToLLVM::isNotObject):
(JSC::FTL::LowerDFGToLLVM::isNotString):
(JSC::FTL::LowerDFGToLLVM::speculateNonNullObject):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITInlines.h:
(JSC::JIT::emitJumpIfCellObject):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_is_object):
(JSC::JIT::emit_op_to_primitive):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_is_object):
(JSC::JIT::emit_op_to_primitive):
(JSC::JIT::compileOpStrictEq):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/Operations.cpp:
(JSC::jsIsObjectTypeOrNull):
(JSC::jsIsObjectType): Deleted.
* runtime/Operations.h:
* tests/stress/constructor-with-return.js: Added.
(Test):
When constructor doesn't return an object, `this` should be returned instead.
In this test, we check all primitives. And test object, array and wrappers.
* tests/stress/dfg-to-primitive-pass-symbol.js: Added.
(toPrimitiveTarget):
(doToPrimitive):
op_to_primitive operation passes Symbol in fast path.
LayoutTests:
Follow the old ret_object_or_this semantics.
When constructor returns an object that masquerades as undefined, we see it as an object.
* js/dom/constructor-with-return-masquerades-expected.txt: Added.
* js/dom/constructor-with-return-masquerades.html: Added.
* js/dom/script-tests/constructor-with-return-masquerades.js: Added.
(Constructor):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@180587 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 0c40677..e4675c2 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1089,6 +1089,16 @@
}
break;
+ case IsObject:
+ if (node->child1()->shouldSpeculateObject()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Phantom, node->origin,
+ Edge(node->child1().node(), ObjectUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<ObjectUse>(node);
+ }
+ break;
+
case GetEnumerableLength: {
fixEdge<CellUse>(node->child1());
break;
@@ -1230,7 +1240,7 @@
case IsUndefined:
case IsBoolean:
case IsNumber:
- case IsObject:
+ case IsObjectOrNull:
case IsFunction:
case CreateArguments:
case PhantomArguments: