WebKit Bugzilla
Attachment 339241 Details for
Bug 182214
: [ESNext][BigInt] Implement "+" and "-" unary operation
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-182214-20180501203153.patch (text/plain), 46.91 KB, created by
Caio Lima
on 2018-05-01 16:31:59 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Caio Lima
Created:
2018-05-01 16:31:59 PDT
Size:
46.91 KB
patch
obsolete
>Subversion Revision: 231215 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 433e5eaa5909e64f896197040bc696d98dcaf52f..e279b8de393f1c46287a658197416e734480fbbe 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,90 @@ >+2018-05-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [ESNext][BigInt] Implement "+" and "-" unary operation >+ https://bugs.webkit.org/show_bug.cgi?id=182214 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This Patch is implementing support to "-" unary operation on BigInt. >+ It is also changing the logic of ASTBuilder::makeNegateNode to >+ calculate BigInt literals with properly sign, avoiding >+ unecessary operation. It required a refactoring into >+ JSBigInt::parseInt to consider the sign as parameter. >+ >+ We are also introducing a new DFG Node called ValueNegate to handle BigInt negate >+ operations. With the introduction of BigInt, it is not true >+ that every negate operation returns a Number. As ArithNegate is a >+ node that considers its result is always a Number, like all other >+ Arith<Operation>, we decided to keep this consistency and use ValueNegate when >+ speculation indicates that the operand is a BigInt. >+ This design is following the same distinction between ArithAdd and >+ ValueAdd. Also, this new node will make simpler the introduction of >+ optimizations when we create speculation paths for BigInt in future >+ patches. >+ >+ In the case of "+" unary operation on BigInt, the current semantic we already have >+ is correctly, since it needs to throw TypeError because of ToNumber call[1]. >+ In such case, we are adding tests to verify other edge cases. >+ >+ [1] - https://tc39.github.io/proposal-bigint/#sec-unary-plus-operator >+ >+ * bytecompiler/BytecodeGenerator.cpp: >+ (JSC::BytecodeGenerator::addBigIntConstant): >+ * bytecompiler/BytecodeGenerator.h: >+ * bytecompiler/NodesCodegen.cpp: >+ (JSC::BigIntNode::jsValue const): >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ * dfg/DFGBackwardsPropagationPhase.cpp: >+ (JSC::DFG::BackwardsPropagationPhase::propagate): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::makeSafe): >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGConstantFoldingPhase.cpp: >+ (JSC::DFG::ConstantFoldingPhase::foldConstants): >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGNode.h: >+ (JSC::DFG::Node::arithNodeFlags): >+ * dfg/DFGNodeType.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileValueNegate): >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileNode): >+ (JSC::FTL::DFG::LowerDFGToB3::compileValueNegate): >+ * jit/JITOperations.cpp: >+ * parser/ASTBuilder.h: >+ (JSC::ASTBuilder::createBigIntWithSign): >+ (JSC::ASTBuilder::createBigIntFromUnaryOperation): >+ (JSC::ASTBuilder::makeNegateNode): >+ * parser/NodeConstructors.h: >+ (JSC::BigIntNode::BigIntNode): >+ * parser/Nodes.h: >+ * runtime/CommonSlowPaths.cpp: >+ (JSC::updateArithProfileForUnaryArithOp): >+ (JSC::SLOW_PATH_DECL): >+ * runtime/JSBigInt.cpp: >+ (JSC::JSBigInt::parseInt): >+ (JSC::JSBigInt::copy): >+ (JSC::JSBigInt::unaryMinus): >+ * runtime/JSBigInt.h: >+ * runtime/JSCJSValueInlines.h: >+ (JSC::JSValue::strictEqualSlowCaseInline): >+ > 2018-04-30 Filip Pizlo <fpizlo@apple.com> > > B3::demoteValues should be able to handle patchpoint terminals >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index d1d4192a8ff3b9a3c708e944cb9dc415eccb91cd..c007e7a8c24359619bcdea39b29cb381892ddfcb 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -3141,11 +3141,12 @@ RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) > return dst; > } > >-JSValue BytecodeGenerator::addBigIntConstant(const Identifier& identifier, uint8_t radix) >+JSValue BytecodeGenerator::addBigIntConstant(const Identifier& identifier, uint8_t radix, bool sign) > { >- return m_bigIntMap.ensure(BigIntMapEntry(identifier.impl(), radix), [&] { >+ return m_bigIntMap.ensure(BigIntMapEntry(identifier.impl(), radix, sign), [&] { > auto scope = DECLARE_CATCH_SCOPE(*vm()); >- JSBigInt* bigIntInMap = JSBigInt::parseInt(nullptr, *vm(), identifier.string(), radix); >+ auto parseIntSign = sign ? JSBigInt::ParseIntSign::Signed : JSBigInt::ParseIntSign::Unsigned; >+ JSBigInt* bigIntInMap = JSBigInt::parseInt(nullptr, *vm(), identifier.string(), radix, parseIntSign); > // FIXME: [ESNext] Enables a way to throw an error on ByteCodeGenerator step > // https://bugs.webkit.org/show_bug.cgi?id=180139 > scope.assertNoException(); >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index b7255ac6216e09dce1ad4d7c669ec546a2cb4673..f6e6eb0a9c1971e43503b52dfd50e9001248689b 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -1023,7 +1023,7 @@ namespace JSC { > void allocateCalleeSaveSpace(); > void allocateAndEmitScope(); > >- using BigIntMapEntry = std::pair<UniquedStringImpl*, uint8_t>; >+ using BigIntMapEntry = std::tuple<UniquedStringImpl*, uint8_t, bool>; > > using NumberMap = HashMap<double, JSValue>; > using IdentifierStringMap = HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash>; >@@ -1112,7 +1112,7 @@ namespace JSC { > > public: > JSString* addStringConstant(const Identifier&); >- JSValue addBigIntConstant(const Identifier&, uint8_t radix); >+ JSValue addBigIntConstant(const Identifier&, uint8_t radix, bool sign); > RegisterID* addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&&); > > Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 07a9c907a7e2130a4244dfa0011f7ca52b06e8a6..6cbaeff62798f11d72c709a9fd90379c4f72bf73 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -124,7 +124,7 @@ JSValue StringNode::jsValue(BytecodeGenerator& generator) const > > JSValue BigIntNode::jsValue(BytecodeGenerator& generator) const > { >- return generator.addBigIntConstant(m_value, m_radix); >+ return generator.addBigIntConstant(m_value, m_radix, m_sign); > } > > // ------------------------------ NumberNode ---------------------------------- >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index c6c98dbaea03a637376fe2244a0726d8c8f0a815..b7c0023c891ac1d7e70c62e2b773b3256c81fe01 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -735,6 +735,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ case ValueNegate: { >+ clobberWorld(node->origin.semantic, clobberLimit); >+ forNode(node).setType(m_graph, SpecBytecodeNumber | SpecBigInt); >+ break; >+ } >+ > case ArithNegate: { > JSValue child = forNode(node->child1()).value(); > switch (node->child1().useKind()) { >diff --git a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >index 9547f89661a25a1fa762d69c8c76769d3861b879..0eae1293c82d0fae5810467e8a27a827107c1307 100644 >--- a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >@@ -307,7 +307,7 @@ private: > node->child2()->mergeFlags(flags); > break; > } >- >+ > case ArithNegate: { > flags &= ~NodeBytecodeUsesAsOther; > >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 1cb947a22a673b8c07a6c01ece10c3c0501a4076..738ec8e328226252846d3a62bfde04ce506e1579 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -934,17 +934,19 @@ private: > node->mergeFlags(NodeMayHaveNonNumberResult); > break; > } >+ case ValueNegate: > case ArithNegate: { >- // We'd like to assert here that the arith profile for the result of negate never >- // sees a non-number, but we can't. It's true that negate never produces a non-number. >- // But sometimes we'll end up grabbing the wrong ArithProfile during OSR exit, and >- // profiling the wrong value, leading the ArithProfile to think it observed a non-number result. > if (arithProfile->lhsObservedType().sawNumber() || arithProfile->didObserveDouble()) > node->mergeFlags(NodeMayHaveDoubleResult); > if (arithProfile->didObserveNegZeroDouble() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) > node->mergeFlags(NodeMayNegZeroInBaseline); > if (arithProfile->didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) > node->mergeFlags(NodeMayOverflowInt32InBaseline); >+ if (arithProfile->didObserveNonNumber()) { >+ // FIXME: We should add support to BigInt into speculation >+ // https://bugs.webkit.org/show_bug.cgi?id=182470 >+ node->mergeFlags(NodeMayHaveNonNumberResult); >+ } > break; > } > >@@ -4685,7 +4687,10 @@ void ByteCodeParser::parseBlock(unsigned limit) > > case op_negate: { > Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand)); >- set(VirtualRegister(currentInstruction[1].u.operand), makeSafe(addToGraph(ArithNegate, op1))); >+ if (op1->hasNumberResult()) >+ set(VirtualRegister(currentInstruction[1].u.operand), makeSafe(addToGraph(ArithNegate, op1))); >+ else >+ set(VirtualRegister(currentInstruction[1].u.operand), makeSafe(addToGraph(ValueNegate, op1))); > NEXT_OPCODE(op_negate); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index 88343530d43d3e5580f1cacf0a3227f5d055b88a..e8f8b2b17a8837a0e61372e81dfb913393967baf 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -622,6 +622,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case ToPrimitive: > case In: > case HasOwnProperty: >+ case ValueNegate: > case ValueAdd: > case SetFunctionName: > case GetDynamicVar: >diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >index cee65623431770f4bf402ada0c877358f800b67d..e4650ac3080f6b420b6378b665ced526bb9de5a7 100644 >--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >@@ -146,14 +146,14 @@ private: > JSValue child2Constant = m_state.forNode(node->child2().node()).value(); > > // FIXME: Revisit this condition when introducing BigInt to JSC. >- auto isNonStringCellConstant = [] (JSValue value) { >- return value && value.isCell() && !value.isString(); >+ auto isNonStringOrBigIntCellConstant = [] (JSValue value) { >+ return value && value.isCell() && !value.isString() && !value.isBigInt(); > }; > >- if (isNonStringCellConstant(child1Constant)) { >+ if (isNonStringOrBigIntCellConstant(child1Constant)) { > node->convertToCompareEqPtr(m_graph.freezeStrong(child1Constant.asCell()), node->child2()); > changed = true; >- } else if (isNonStringCellConstant(child2Constant)) { >+ } else if (isNonStringOrBigIntCellConstant(child2Constant)) { > node->convertToCompareEqPtr(m_graph.freezeStrong(child2Constant.asCell()), node->child1()); > changed = true; > } >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index 8287fac07c4a53aeb5321e8fbbf55488eb83fa82..6ef2d32375e9c1338f9d511a883c475e1b75b8b0 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -97,6 +97,7 @@ bool doesGC(Graph& graph, Node* node) > case ArithFRound: > case ArithUnary: > case ValueAdd: >+ case ValueNegate: > case TryGetById: > case GetById: > case GetByIdFlush: >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index dd95388cfaf2d816cd7f2d9abffbfaf060ef490b..b907dbaedf1c728e6e7271fa2b5f0e033b8b2c66 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -146,7 +146,45 @@ private: > } > break; > } >+ >+ case ValueNegate: { >+ if (node->child1()->shouldSpeculateInt32OrBoolean() && node->canSpeculateInt32(FixupPass)) { >+ node->setOp(ArithNegate); >+ fixIntOrBooleanEdge(node->child1()); >+ if (bytecodeCanTruncateInteger(node->arithNodeFlags())) >+ node->setArithMode(Arith::Unchecked); >+ else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) >+ node->setArithMode(Arith::CheckOverflow); >+ else >+ node->setArithMode(Arith::CheckOverflowAndNegativeZero); >+ node->setResult(NodeResultInt32); >+ node->clearFlags(NodeMustGenerate); >+ break; >+ } > >+ if (m_graph.unaryArithShouldSpeculateAnyInt(node, FixupPass)) { >+ node->setOp(ArithNegate); >+ fixEdge<Int52RepUse>(node->child1()); >+ if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) >+ node->setArithMode(Arith::CheckOverflow); >+ else >+ node->setArithMode(Arith::CheckOverflowAndNegativeZero); >+ node->setResult(NodeResultInt52); >+ node->clearFlags(NodeMustGenerate); >+ break; >+ } >+ if (node->child1()->shouldSpeculateNotCell()) { >+ node->setOp(ArithNegate); >+ fixDoubleOrBooleanEdge(node->child1()); >+ node->setResult(NodeResultDouble); >+ node->clearFlags(NodeMustGenerate); >+ } else { >+ fixEdge<UntypedUse>(node->child1()); >+ node->setResult(NodeResultJS); >+ } >+ break; >+ } >+ > case ValueAdd: { > if (attemptToMakeIntegerAdd(node)) { > node->setOp(ArithAdd); >diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h >index 4d97cd3ab127d30bbf46ac739edbf1bea85c130d..6fea62e35d08780e8172a949fe670cfeb609aea1 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.h >+++ b/Source/JavaScriptCore/dfg/DFGNode.h >@@ -1088,7 +1088,7 @@ public: > NodeFlags arithNodeFlags() > { > NodeFlags result = m_flags & NodeArithFlagsMask; >- if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc || op() == DoubleAsInt32) >+ if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc || op() == DoubleAsInt32 || op() == ValueNegate) > return result; > return result & ~NodeBytecodeNeedsNegZero; > } >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index 075b8af6769ecb8823638b5ad6c216d15e9f86c7..a76ad7aca32cd7bf826d5fd83d2c81d437ec3999 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -162,6 +162,9 @@ namespace JSC { namespace DFG { > macro(ArithSqrt, NodeResultDouble | NodeMustGenerate) \ > macro(ArithUnary, NodeResultDouble | NodeMustGenerate) \ > \ >+ /* BigInt is a valid operand for negate operations */\ >+ macro(ValueNegate, NodeResultJS | NodeMustGenerate) \ >+ \ > /* Add of values may either be arithmetic, or result in string concatenation. */\ > macro(ValueAdd, NodeResultJS | NodeMustGenerate) \ > \ >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index d4bbb4a61ca8915db081cca3cd6b51b5ccd26156..ea6594844f4c800fac8cbf3cc4934e76627991d9 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -245,6 +245,7 @@ private: > break; > } > >+ case ValueNegate: > case ArithNegate: { > SpeculatedType prediction = node->child1()->prediction(); > if (prediction) { >@@ -256,6 +257,8 @@ private: > changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction())); > else { > changed |= mergePrediction(SpecInt32Only); >+ if (node->op() == ValueNegate && node->mayHaveNonNumberResult()) >+ changed |= mergePrediction(SpecBigInt); > if (node->mayHaveDoubleResult()) > changed |= mergePrediction(SpecBytecodeDouble); > } >@@ -1035,6 +1038,7 @@ private: > case GetLocal: > case SetLocal: > case UInt32ToNumber: >+ case ValueNegate: > case ValueAdd: > case ArithAdd: > case ArithSub: >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index 21db340a98289fc4cf9ea2204daa0c85fdacc9e6..7803ea32f855383e51cc7e7460ab03618003dc57 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -219,6 +219,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node) > case ArithCeil: > case ArithTrunc: > case ArithUnary: >+ case ValueNegate: > case ValueAdd: > case TryGetById: > case DeleteById: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 92784e8fb9bb27f905c180cb8e3ee003fdaab3fa..c8346d3e470edf8489e0bfdb8ce24d7638b4027c 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -4520,6 +4520,19 @@ void SpeculativeJIT::compileArithSub(Node* node) > } > } > >+void SpeculativeJIT::compileValueNegate(Node* node) >+{ >+ CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); >+ ArithProfile* arithProfile = baselineCodeBlock->arithProfileForBytecodeOffset(node->origin.semantic.bytecodeIndex); >+ Instruction* instruction = &baselineCodeBlock->instructions()[node->origin.semantic.bytecodeIndex]; >+ JITNegIC* negIC = m_jit.codeBlock()->addJITNegIC(arithProfile, instruction); >+ auto repatchingFunction = operationArithNegateOptimize; >+ auto nonRepatchingFunction = operationArithNegate; >+ bool needsScratchGPRReg = true; >+ compileMathIC(node, negIC, needsScratchGPRReg, repatchingFunction, nonRepatchingFunction); >+ return; >+} >+ > void SpeculativeJIT::compileArithNegate(Node* node) > { > switch (node->child1().useKind()) { >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index bb93965150a9a2d3cdbf1c2e3714d99d94c078e9..5b827bc3b719ea0cd765416cc96ee6ecfdeb6e8a 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -1345,6 +1345,7 @@ public: > void compileArithAbs(Node*); > void compileArithClz32(Node*); > void compileArithSub(Node*); >+ void compileValueNegate(Node*); > void compileArithNegate(Node*); > void compileArithMul(Node*); > void compileArithDiv(Node*); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index 945e40c3fbbe74cf435f805263c7709d45eb6dc6..159983d31f879dab99a86bbd2d1a55863de2cf15 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -2084,6 +2084,10 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ValueNegate: >+ compileValueNegate(node); >+ break; >+ > case ValueAdd: > compileValueAdd(node); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index 2af307fe1516179d7806f2f2a72db3edb997bdd6..dc45d11a6151db441c22e1be70a74c618fca6aaa 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -2251,6 +2251,10 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ValueNegate: >+ compileValueNegate(node); >+ break; >+ > case ValueAdd: > compileValueAdd(node); > break; >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index 2f74baecfe632a638f5e67f51cdbfbff053e72da..a149e4dd69ce1365bde6bf85131494fefd225623 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -86,6 +86,7 @@ inline CapabilityLevel canCompile(Node* node) > case GetGlobalVar: > case GetGlobalLexicalVariable: > case PutGlobalVariable: >+ case ValueNegate: > case ValueAdd: > case StrCat: > case ArithAdd: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index eef68b99d4d03bd23573e320d3d14c7251193140..823b1868603076eb98b4210549e4f6dfbbfb50d1 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -583,6 +583,9 @@ private: > case ToThis: > compileToThis(); > break; >+ case ValueNegate: >+ compileValueNegate(); >+ break; > case ValueAdd: > compileValueAdd(); > break; >@@ -2699,7 +2702,18 @@ private: > LValue result = vmCall(Double, m_out.operation(operationArithFRound), m_callFrame, argument); > setDouble(result); > } >- >+ >+ void compileValueNegate() >+ { >+ DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse); >+ CodeBlock* baselineCodeBlock = m_ftlState.graph.baselineCodeBlockFor(m_node->origin.semantic); >+ ArithProfile* arithProfile = baselineCodeBlock->arithProfileForBytecodeOffset(m_node->origin.semantic.bytecodeIndex); >+ Instruction* instruction = &baselineCodeBlock->instructions()[m_node->origin.semantic.bytecodeIndex]; >+ auto repatchingFunction = operationArithNegateOptimize; >+ auto nonRepatchingFunction = operationArithNegate; >+ compileUnaryMathIC<JITNegGenerator>(arithProfile, instruction, repatchingFunction, nonRepatchingFunction); >+ } >+ > void compileArithNegate() > { > switch (m_node->child1().useKind()) { >diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp >index 365bb73755a7a454cf085436c6378d1bf73fec4b..95dba8ce726bdbcc9939f83ea8bc8cabfad8cf86 100644 >--- a/Source/JavaScriptCore/jit/JITOperations.cpp >+++ b/Source/JavaScriptCore/jit/JITOperations.cpp >@@ -2663,7 +2663,14 @@ ALWAYS_INLINE static EncodedJSValue unprofiledNegate(ExecState* exec, EncodedJSV > NativeCallFrameTracer tracer(&vm, exec); > > JSValue operand = JSValue::decode(encodedOperand); >- double number = operand.toNumber(exec); >+ >+ JSValue primValue = operand.toPrimitive(exec, PreferNumber); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ >+ if (primValue.isBigInt()) >+ return JSValue::encode(JSValue(JSBigInt::unaryMinus(exec, asBigInt(primValue)))); >+ >+ double number = primValue.toNumber(exec); > RETURN_IF_EXCEPTION(scope, encodedJSValue()); > return JSValue::encode(jsNumber(-number)); > } >@@ -2676,9 +2683,19 @@ ALWAYS_INLINE static EncodedJSValue profiledNegate(ExecState* exec, EncodedJSVal > > JSValue operand = JSValue::decode(encodedOperand); > arithProfile.observeLHS(operand); >- double number = operand.toNumber(exec); >+ >+ JSValue primValue = operand.toPrimitive(exec); > RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ >+ if (primValue.isBigInt()) { >+ JSValue result = JSValue(JSBigInt::unaryMinus(exec, asBigInt(primValue))); >+ arithProfile.observeResult(result); >+ >+ return JSValue::encode(result); >+ } > >+ double number = primValue.toNumber(exec); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); > JSValue result = jsNumber(-number); > arithProfile.observeResult(result); > return JSValue::encode(result); >@@ -2712,7 +2729,16 @@ EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(ExecState* exe > exec->codeBlock()->dumpMathICStats(); > #endif > >- double number = operand.toNumber(exec); >+ JSValue primValue = operand.toPrimitive(exec); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ >+ if (primValue.isBigInt()) { >+ JSValue result = JSValue(JSBigInt::unaryMinus(exec, asBigInt(primValue))); >+ arithProfile->observeResult(result); >+ return JSValue::encode(result); >+ } >+ >+ double number = primValue.toNumber(exec); > RETURN_IF_EXCEPTION(scope, encodedJSValue()); > JSValue result = jsNumber(-number); > arithProfile->observeResult(result); >@@ -2735,7 +2761,15 @@ EncodedJSValue JIT_OPERATION operationArithNegateOptimize(ExecState* exec, Encod > exec->codeBlock()->dumpMathICStats(); > #endif > >- double number = operand.toNumber(exec); >+ JSValue primValue = operand.toPrimitive(exec); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ >+ if (primValue.isBigInt()) { >+ JSValue result = JSValue(JSBigInt::unaryMinus(exec, asBigInt(primValue))); >+ return JSValue::encode(result); >+ } >+ >+ double number = primValue.toNumber(exec); > RETURN_IF_EXCEPTION(scope, encodedJSValue()); > return JSValue::encode(jsNumber(-number)); > } >diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h >index 5c6dd33198ca2f94505358cdf94b605c9be7e812..65ad1070fbb2203dc73c5fe73971c778471747a5 100644 >--- a/Source/JavaScriptCore/parser/ASTBuilder.h >+++ b/Source/JavaScriptCore/parser/ASTBuilder.h >@@ -1072,6 +1072,10 @@ private: > { > return new (m_parserArena) DoubleNode(location, d); > } >+ ExpressionNode* createBigIntWithSign(const JSTokenLocation& location, const Identifier& bigInt, uint8_t radix, bool sign) >+ { >+ return new (m_parserArena) BigIntNode(location, bigInt, radix, sign); >+ } > ExpressionNode* createNumberFromBinaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNodeA, const NumberNode& originalNodeB) > { > if (originalNodeA.isIntegerNode() && originalNodeB.isIntegerNode()) >@@ -1084,6 +1088,10 @@ private: > return createIntegerLikeNumber(location, value); > return createDoubleLikeNumber(location, value); > } >+ ExpressionNode* createBigIntFromUnaryOperation(const JSTokenLocation& location, bool sign, const BigIntNode& originalNode) >+ { >+ return createBigIntWithSign(location, originalNode.identifier(), originalNode.radix(), sign); >+ } > > void tryInferNameInPattern(DestructuringPattern pattern, ExpressionNode* defaultValue) > { >@@ -1156,6 +1164,11 @@ ExpressionNode* ASTBuilder::makeNegateNode(const JSTokenLocation& location, Expr > return createNumberFromUnaryOperation(location, -numberNode.value(), numberNode); > } > >+ if (n->isBigInt()) { >+ const BigIntNode& bigIntNode = static_cast<const BigIntNode&>(*n); >+ return createBigIntFromUnaryOperation(location, !bigIntNode.sign(), bigIntNode); >+ } >+ > return new (m_parserArena) NegateNode(location, n); > } > >diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h >index e6f03b65b75d00252f043ec9931fbba504b93cde..573a6628cf3b00415d2c7e6d313f812a94775c1a 100644 >--- a/Source/JavaScriptCore/parser/NodeConstructors.h >+++ b/Source/JavaScriptCore/parser/NodeConstructors.h >@@ -97,6 +97,15 @@ namespace JSC { > : ConstantNode(location, ResultType::bigIntType()) > , m_value(value) > , m_radix(radix) >+ , m_sign(false) >+ { >+ } >+ >+ inline BigIntNode::BigIntNode(const JSTokenLocation& location, const Identifier& value, uint8_t radix, bool sign) >+ : ConstantNode(location, ResultType::bigIntType()) >+ , m_value(value) >+ , m_radix(radix) >+ , m_sign(sign) > { > } > >diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h >index 6c0f2dbd0c11adb42c0dec69e4d0dee1adc71cd1..4365637c882155b0ccca99c2fc2e5587740fc783 100644 >--- a/Source/JavaScriptCore/parser/Nodes.h >+++ b/Source/JavaScriptCore/parser/Nodes.h >@@ -345,14 +345,20 @@ namespace JSC { > class BigIntNode final : public ConstantNode { > public: > BigIntNode(const JSTokenLocation&, const Identifier&, uint8_t radix); >+ BigIntNode(const JSTokenLocation&, const Identifier&, uint8_t radix, bool sign); > const Identifier& value() { return m_value; } > >+ const Identifier& identifier() const { return m_value; } >+ uint8_t radix() const { return m_radix; } >+ bool sign() const { return m_sign; } >+ > private: > bool isBigInt() const final { return true; } > JSValue jsValue(BytecodeGenerator&) const final; > > const Identifier& m_value; > const uint8_t m_radix; >+ const bool m_sign; > }; > > class ThrowableExpressionData { >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index e6c1a44d67f42145285fb0d200745dbcb3a7df77..6e4034af0f4f667aa812f17da3c4afc6f144dfd2 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -364,26 +364,29 @@ static void updateArithProfileForUnaryArithOp(Instruction* pc, JSValue result, J > { > ArithProfile& profile = *bitwise_cast<ArithProfile*>(&pc[3].u.operand); > profile.observeLHS(operand); >- ASSERT(result.isNumber()); >- if (!result.isInt32()) { >- if (operand.isInt32()) >- profile.setObservedInt32Overflow(); >- >- double doubleVal = result.asNumber(); >- if (!doubleVal && std::signbit(doubleVal)) >- profile.setObservedNegZeroDouble(); >- else { >- profile.setObservedNonNegZeroDouble(); >- >- // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. >- // Therefore, we will get a false positive if the result is that value. This is intentionally >- // done to simplify the checking algorithm. >- static const int64_t int52OverflowPoint = (1ll << 51); >- int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); >- if (int64Val >= int52OverflowPoint) >- profile.setObservedInt52Overflow(); >+ ASSERT(result.isNumber() || result.isBigInt()); >+ if (result.isNumber()) { >+ if (!result.isInt32()) { >+ if (operand.isInt32()) >+ profile.setObservedInt32Overflow(); >+ >+ double doubleVal = result.asNumber(); >+ if (!doubleVal && std::signbit(doubleVal)) >+ profile.setObservedNegZeroDouble(); >+ else { >+ profile.setObservedNonNegZeroDouble(); >+ >+ // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. >+ // Therefore, we will get a false positive if the result is that value. This is intentionally >+ // done to simplify the checking algorithm. >+ static const int64_t int52OverflowPoint = (1ll << 51); >+ int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); >+ if (int64Val >= int52OverflowPoint) >+ profile.setObservedInt52Overflow(); >+ } > } >- } >+ } else >+ profile.setObservedNonNumber(); > } > #else > static void updateArithProfileForUnaryArithOp(Instruction*, JSValue, JSValue) { } >@@ -393,7 +396,18 @@ SLOW_PATH_DECL(slow_path_negate) > { > BEGIN(); > JSValue operand = OP_C(2).jsValue(); >- JSValue result = jsNumber(-operand.toNumber(exec)); >+ JSValue primValue = operand.toPrimitive(exec, PreferNumber); >+ CHECK_EXCEPTION(); >+ >+ if (primValue.isBigInt()) { >+ JSValue result = JSValue(JSBigInt::unaryMinus(exec, asBigInt(primValue))); >+ RETURN_WITH_PROFILING(result, { >+ updateArithProfileForUnaryArithOp(pc, result, operand); >+ }); >+ } >+ >+ JSValue result = jsNumber(-primValue.toNumber(exec)); >+ CHECK_EXCEPTION(); > RETURN_WITH_PROFILING(result, { > updateArithProfileForUnaryArithOp(pc, result, operand); > }); >diff --git a/Source/JavaScriptCore/runtime/JSBigInt.cpp b/Source/JavaScriptCore/runtime/JSBigInt.cpp >index c51c8b210d93c17f96400efe77daa672a259a6d3..a41c4777f96c9eb75f36fcd943e40886a734f29d 100644 >--- a/Source/JavaScriptCore/runtime/JSBigInt.cpp >+++ b/Source/JavaScriptCore/runtime/JSBigInt.cpp >@@ -216,11 +216,11 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, StringView s) > return parseInt(state, s.characters16(), s.length()); > } > >-JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t radix) >+JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t radix, ParseIntSign sign) > { > if (s.is8Bit()) >- return parseInt(state, vm, s.characters8(), s.length(), 0, radix, false); >- return parseInt(state, vm, s.characters16(), s.length(), 0, radix, false); >+ return parseInt(state, vm, s.characters8(), s.length(), 0, radix, sign, DisallowEmptyString); >+ return parseInt(state, vm, s.characters16(), s.length(), 0, radix, sign, DisallowEmptyString); > } > > String JSBigInt::toString(ExecState& state, int radix) >@@ -246,6 +246,27 @@ void JSBigInt::inplaceMultiplyAdd(uintptr_t factor, uintptr_t summand) > internalMultiplyAdd(this, factor, summand, length(), this); > } > >+JSBigInt* JSBigInt::copy(ExecState* state, JSBigInt* x) >+{ >+ ASSERT(!x->isZero()); >+ >+ VM& vm = state->vm(); >+ JSBigInt* result = JSBigInt::createWithLength(vm, x->length()); >+ memcpy(result->dataStorage(), x->dataStorage(), x->length() * sizeof(Digit)); >+ result->setSign(x->sign()); >+ return result; >+} >+ >+JSBigInt* JSBigInt::unaryMinus(ExecState* state, JSBigInt* x) >+{ >+ if (x->isZero()) >+ return x; >+ >+ JSBigInt* result = copy(state, x); >+ result->setSign(!x->sign()); >+ return result; >+} >+ > #if USE(JSVALUE32_64) > #define HAVE_TWO_DIGIT 1 > typedef uint64_t TwoDigit; >@@ -719,7 +740,7 @@ size_t JSBigInt::offsetOfData() > } > > template <typename CharType> >-JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, int length) >+JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, int length) > { > VM& vm = state->vm(); > >@@ -730,42 +751,37 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, int length) > // Check Radix from frist characters > if (static_cast<unsigned>(p) + 1 < static_cast<unsigned>(length) && data[p] == '0') { > if (isASCIIAlphaCaselessEqual(data[p + 1], 'b')) >- return parseInt(state, vm, data, length, p + 2, 2, false); >+ return parseInt(state, vm, data, length, p + 2, 2, Unsigned, DisallowEmptyString); > > if (isASCIIAlphaCaselessEqual(data[p + 1], 'x')) >- return parseInt(state, vm, data, length, p + 2, 16, false); >+ return parseInt(state, vm, data, length, p + 2, 16, Unsigned, DisallowEmptyString); > > if (isASCIIAlphaCaselessEqual(data[p + 1], 'o')) >- return parseInt(state, vm, data, length, p + 2, 8, false); >+ return parseInt(state, vm, data, length, p + 2, 8, Unsigned, DisallowEmptyString); > } > >- bool sign = false; >+ ParseIntSign sign = Unsigned; > if (p < length) { > if (data[p] == '+') > ++p; > else if (data[p] == '-') { >- sign = true; >+ sign = Signed; > ++p; > } > } > >- JSBigInt* result = parseInt(state, vm, data, length, p, 10); >- >- if (result && !result->isZero()) >- result->setSign(sign); >- >- return result; >+ return parseInt(state, vm, data, length, p, 10, sign); > } > > template <typename CharType> >-JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int length, int startIndex, int radix, bool allowEmptyString) >+JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int length, int startIndex, int radix, ParseIntSign sign, ParseIntMode parseMode) > { > ASSERT(length >= 0); > int p = startIndex; > > auto scope = DECLARE_THROW_SCOPE(vm); > >- if (!allowEmptyString && startIndex == length) { >+ if (parseMode != ParseIntMode::AllowEmptyString && startIndex == length) { > ASSERT(state); > throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt")); > return nullptr; >@@ -808,6 +824,7 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int lengt > result->inplaceMultiplyAdd(static_cast<Digit>(radix), static_cast<Digit>(digit)); > } > >+ result->setSign(sign == ParseIntSign::Signed ? true : false); > if (p == length) > return result->rightTrim(vm); > >diff --git a/Source/JavaScriptCore/runtime/JSBigInt.h b/Source/JavaScriptCore/runtime/JSBigInt.h >index 298c348cd2b4e5ab5c92c7bb799a30f3ebfa6231..09efdc7a0d706d404d518b1dd958aa0ca7dfcb84 100644 >--- a/Source/JavaScriptCore/runtime/JSBigInt.h >+++ b/Source/JavaScriptCore/runtime/JSBigInt.h >@@ -73,7 +73,10 @@ public: > void setLength(int length) { m_length = length; } > int length() const { return m_length; } > >- static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix); >+ enum ParseIntMode { DisallowEmptyString, AllowEmptyString }; >+ enum ParseIntSign { Unsigned, Signed }; >+ >+ static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix, ParseIntSign = Unsigned); > static JSBigInt* parseInt(ExecState*, StringView); > > std::optional<uint8_t> singleDigitValueForString(); >@@ -86,6 +89,8 @@ public: > > JSObject* toObject(ExecState*, JSGlobalObject*) const; > >+ static JSBigInt* unaryMinus(ExecState*, JSBigInt* x); >+ > private: > using Digit = uintptr_t; > static constexpr const int bitsPerByte = 8; >@@ -120,10 +125,12 @@ private: > static JSBigInt* parseInt(ExecState*, CharType* data, int length); > > template <typename CharType> >- static JSBigInt* parseInt(ExecState*, VM&, CharType* data, int length, int startIndex, int radix, bool allowEmptyString = true); >+ static JSBigInt* parseInt(ExecState*, VM&, CharType* data, int length, int startIndex, int radix, ParseIntSign = Signed, ParseIntMode = AllowEmptyString); > > static JSBigInt* allocateFor(ExecState*, VM&, int radix, int charcount); > >+ static JSBigInt* copy(ExecState*, JSBigInt*); >+ > JSBigInt* rightTrim(VM&); > > void inplaceMultiplyAdd(Digit multiplier, Digit part); >diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >index d0d3799fb39ec3182dcd2149607060e35b5b279a..bddf7c0808b2ee3d28d003ff1640456b85e3a8a5 100644 >--- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >+++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h >@@ -1051,7 +1051,7 @@ ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v > if (v1.asCell()->isString() && v2.asCell()->isString()) > return asString(v1)->equal(exec, asString(v2)); > if (v1.isBigInt() && v2.isBigInt()) >- return JSBigInt::equals(asBigInt(v1.asCell()), asBigInt(v2.asCell())); >+ return JSBigInt::equals(asBigInt(v1), asBigInt(v2)); > return v1 == v2; > } > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 89e551d4294bfa7a197c0c210c8fce3475c74663..7376e6e336abba5138d2adc1589ee9dd4fcf882d 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,14 @@ >+2018-05-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [ESNext][BigInt] Implement "+" and "-" unary operation >+ https://bugs.webkit.org/show_bug.cgi?id=182214 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/big-int-negate-basic.js: Added. >+ * stress/big-int-negate-jit.js: Added. >+ * stress/big-int-unary-plus.js: Added. >+ > 2018-05-01 Robin Morisset <rmorisset@apple.com> > > Correctly detect string overflow when using the 'Function' constructor >diff --git a/JSTests/stress/big-int-negate-basic.js b/JSTests/stress/big-int-negate-basic.js >new file mode 100644 >index 0000000000000000000000000000000000000000..59ba3c4724dbf01089afd58360a1b5fa72c8b72f >--- /dev/null >+++ b/JSTests/stress/big-int-negate-basic.js >@@ -0,0 +1,71 @@ >+//@ runBigIntEnabled >+// Original tests from https://github.com/tc39/test262/blob/master/test/language/expressions/unary-minus/bigint.js >+ >+function assert(a, b, message) { >+ if (a !== b) >+ throw new Error(message); >+} >+ >+function assertNotEqual(a, b, message) { >+ if (a === b) >+ throw new Error(message); >+} >+ >+assert(-0n, 0n, "-0n === 0n"); >+assert(-(0n), 0n, "-(0n) === 0n"); >+assertNotEqual(-1n, 1n, "-1n !== 1n"); >+assert(-(1n), -1n, "-(1n) === -1n"); >+assertNotEqual(-(1n), 1n, "-(1n) !== 1n"); >+assert(-(-1n), 1n, "-(-1n) === 1n"); >+assertNotEqual(-(-1n), -1n, "-(-1n) !== -1n"); >+assert(- - 1n, 1n, "- - 1n === 1n"); >+assertNotEqual(- - 1n, -1n, "- - 1n !== -1n"); >+assert(-(0x1fffffffffffff01n), -0x1fffffffffffff01n, "-(0x1fffffffffffff01n) === -0x1fffffffffffff01n"); >+assertNotEqual(-(0x1fffffffffffff01n), 0x1fffffffffffff01n, "-(0x1fffffffffffff01n) !== 0x1fffffffffffff01n"); >+assertNotEqual(-(0x1fffffffffffff01n), -0x1fffffffffffff00n, "-(0x1fffffffffffff01n) !== -0x1fffffffffffff00n"); >+ >+// Non-primitive cases >+ >+assert(-Object(1n), -1n, "-Object(1n) === -1n"); >+assertNotEqual(-Object(1n), 1n, "-Object(1n) !== 1n"); >+assertNotEqual(-Object(1n), Object(-1n), "-Object(1n) !== Object(-1n)"); >+assert(-Object(-1n), 1n, "-Object(-1n) === 1n"); >+assertNotEqual(-Object(-1n), -1n, "-Object(-1n) !== -1n"); >+assertNotEqual(-Object(-1n), Object(1n), "-Object(-1n) !== Object(1n)"); >+ >+let obj = { >+ [Symbol.toPrimitive]: function() { >+ return 1n; >+ }, >+ valueOf: function() { >+ throw new Error("Should never be called"); >+ }, >+ toString: function() { >+ throw new Error("Should never be called"); >+ } >+}; >+assert(-obj, -1n, "@@toPrimitive not called properly"); >+ >+obj = { >+ valueOf: function() { >+ return 1n; >+ }, >+ toString: function() { >+ throw new Error("Should never be called"); >+ } >+} >+assert(-obj, -1n, "valueOf not called properly"); >+ >+obj = { >+ toString: function() { >+ return 1n; >+ } >+}; >+ >+assert(-obj, -1n, "-{toString: function() { return 1n; }} === -1n"); >+ >+let x = 1n; >+let y = -x; >+let z = -y; >+assert(x, z, "-(-x) !== z"); >+ >diff --git a/JSTests/stress/big-int-negate-jit.js b/JSTests/stress/big-int-negate-jit.js >new file mode 100644 >index 0000000000000000000000000000000000000000..ab497831c965e87436ab904078bedb0269b58ca0 >--- /dev/null >+++ b/JSTests/stress/big-int-negate-jit.js >@@ -0,0 +1,48 @@ >+//@ skip if not $jitTests >+//@ runBigIntEnabled >+ >+function assert(a, b) { >+ if (a !== b) >+ throw new Error("Bad!"); >+} >+ >+function negateBigInt(n) { >+ return -n; >+} >+noInline(negateBigInt); >+ >+for (let i = 0; i < 100000; i++) { >+ assert(negateBigInt(100n), -100n); >+ assert(negateBigInt(-0x1fffffffffffff01n), 0x1fffffffffffff01n); >+} >+ >+if (numberOfDFGCompiles(negateBigInt) > 1) >+ throw "Failed negateBigInt(). We should have compiled a single negate for the BigInt type."; >+ >+function negateBigIntSpecializedToInt(n) { >+ return -n; >+} >+noInline(negateBigIntSpecializedToInt); >+ >+for (let i = 0; i < 100000; i++) { >+ negateBigIntSpecializedToInt(100); >+} >+ >+assert(negateBigIntSpecializedToInt(100n), -100n); >+ >+// Testing case mixing int and BigInt speculations >+function mixedSpeculationNegateBigInt(n, arr) { >+ return -(-(-n)); >+} >+noInline(mixedSpeculationNegateBigInt); >+ >+for (let i = 0; i < 100000; i++) { >+ if (i % 2) >+ assert(mixedSpeculationNegateBigInt(100), -100); >+ else >+ assert(mixedSpeculationNegateBigInt(-0x1fffffffffffff01n), 0x1fffffffffffff01n); >+} >+ >+if (numberOfDFGCompiles(mixedSpeculationNegateBigInt) > 1) >+ throw "Failed negateBigInt(). We should have compiled a single negate for the BigInt type."; >+ >diff --git a/JSTests/stress/big-int-unary-plus.js b/JSTests/stress/big-int-unary-plus.js >new file mode 100644 >index 0000000000000000000000000000000000000000..4d4ac37b9e5373e6b18b91983cdd372e55396240 >--- /dev/null >+++ b/JSTests/stress/big-int-unary-plus.js >@@ -0,0 +1,51 @@ >+//@ runBigIntEnabled >+ >+function assert(a) { >+ if (!a) >+ throw new Error("Bad!") >+} >+ >+function assertTypeError(input) { >+ try { >+ let a = +input; >+ assert(false); >+ } catch(e) { >+ assert(e instanceof TypeError); >+ } >+} >+ >+assertTypeError(10n); >+assertTypeError(-10n); >+assertTypeError(Object(10n)); >+assertTypeError(Object(-10n)); >+ >+let obj = { >+ [Symbol.toPrimitive]: function() { >+ return 1n; >+ }, >+ valueOf: function() { >+ throw new Error("Should never be called"); >+ }, >+ toString: function() { >+ throw new Error("Should never be called"); >+ } >+}; >+assertTypeError(obj); >+ >+obj = { >+ valueOf: function() { >+ return 1n; >+ }, >+ toString: function() { >+ throw new Error("Should never be called"); >+ } >+}; >+assertTypeError(obj); >+ >+obj = { >+ toString: function() { >+ return 1n; >+ } >+}; >+assertTypeError(obj); >+
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 182214
:
332473
|
332485
|
332486
|
333018
|
333056
|
335475
|
337703
|
337718
|
338072
|
338075
|
338579
|
338956
|
339233
|
339241
|
339254
|
339646
|
340157
|
340263
|
340265
|
340270
|
340808
|
340812
|
341279
|
341440