[ES7] Introduce exponentiation expression
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Jul 2016 07:33:28 +0000 (07:33 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Jul 2016 07:33:28 +0000 (07:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159969

Reviewed by Saam Barati.

This patch implements the exponentiation expression, e.g. `x ** y`.
The exponentiation expression is introduced in ECMA262 2016 and ECMA262 2016
is already released. So this is not the draft spec.

The exponentiation expression has 2 interesting points.

1. Right associative

    To follow the Math expression, ** operator is right associative.
    When we execute `x ** y ** z`, this is handled as `x ** (y ** z)`, not `(x ** y) ** z`.
    This patch introduces the right associativity to the binary operator and handles it
    in the operator precedence parser in Parser.cpp.

2. LHS of the exponentiation expression is UpdateExpression

    ExponentiationExpression[Yield]:
        UnaryExpression[?Yield]
        UpdateExpression[?Yield] ** ExponentiationExpression[?Yield]

    As we can see, the left hand side of the ExponentiationExpression is UpdateExpression, not UnaryExpression.
    It means that `+x ** y` becomes a syntax error. This is intentional. Without superscript in JS,
    `-x**y` is confusing between `-(x ** y)` and `(-x) ** y`. So ECMA262 intentionally avoids UnaryExpression here.
    If we need to use a negated value, we need to write parentheses explicitly e.g. `(-x) ** y`.
    In this patch, we ensure that the left hand side is not an unary expression by checking an operator in
    parseBinaryExpression. This works since `**` has the highest operator precedence in the binary operators.

We introduce a new bytecode, op_pow. That simply works as similar as the other binary operators.
And it is converted to ArithPow in DFG and handled in DFG and FTL.
In this patch, we take the approach just introducing a new bytecode instead of calling Math.pow.
This is because we would like to execute ToNumber in the caller side, not in the callee (Math.pow) side.
And we don't want to compile ** into the following.

    lhsNumber = to_number (lhs)
    rhsNumber = to_number (rhs)
    call Math.pow(lhsNumber, rhsNumber)

We ensure that this patch passes all the test262 tests related to the exponentiation expression.

The only sensitive part to the performance is the parser changes.
So we measured the code-load performance and it is neutral in my x64 Linux box (hanayamata).

    Collected 30 samples per benchmark/VM, with 30 VM invocations per benchmark. Emitted a call to
    gc() between sample measurements. Used 1 benchmark iteration per VM invocation for warm-up. Used
    the jsc-specific preciseTime() function to get microsecond-level timing. Reporting benchmark
    execution times with 95% confidence intervals in milliseconds.

                             baseline                  patched

    closure              0.60499+-0.00250          0.60180+-0.00244
    jquery               7.89175+-0.02433    ?     7.91287+-0.04759       ?

    <geometric>          2.18499+-0.00523          2.18207+-0.00689         might be 1.0013x faster

* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecompiler/NodesCodegen.cpp:
(JSC::emitReadModifyAssignment):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_pow):
* llint/LowLevelInterpreter.asm:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::operatorStackShouldReduce):
(JSC::ASTBuilder::makePowNode):
(JSC::ASTBuilder::makeMultNode):
(JSC::ASTBuilder::makeDivNode):
(JSC::ASTBuilder::makeModNode):
(JSC::ASTBuilder::makeSubNode):
(JSC::ASTBuilder::makeBinaryNode):
(JSC::ASTBuilder::operatorStackHasHigherPrecedence): Deleted.
* parser/Lexer.cpp:
(JSC::Lexer<T>::lex):
* parser/NodeConstructors.h:
(JSC::PowNode::PowNode):
* parser/Nodes.h:
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseAssignmentExpression):
(JSC::isUnaryOpExcludingUpdateOp):
(JSC::Parser<LexerType>::parseBinaryExpression):
(JSC::isUnaryOp): Deleted.
* parser/ParserTokens.h:
(JSC::isUpdateOp):
(JSC::isUnaryOp):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* tests/stress/pow-basics.js: Added.
(valuesAreClose):
(mathPowDoubleDouble1):
(mathPowDoubleInt1):
(test1):
(mathPowDoubleDouble2):
(mathPowDoubleInt2):
(test2):
(mathPowDoubleDouble3):
(mathPowDoubleInt3):
(test3):
(mathPowDoubleDouble4):
(mathPowDoubleInt4):
(test4):
(mathPowDoubleDouble5):
(mathPowDoubleInt5):
(test5):
(mathPowDoubleDouble6):
(mathPowDoubleInt6):
(test6):
(mathPowDoubleDouble7):
(mathPowDoubleInt7):
(test7):
(mathPowDoubleDouble8):
(mathPowDoubleInt8):
(test8):
(mathPowDoubleDouble9):
(mathPowDoubleInt9):
(test9):
(mathPowDoubleDouble10):
(mathPowDoubleInt10):
(test10):
(mathPowDoubleDouble11):
(mathPowDoubleInt11):
(test11):
* tests/stress/pow-coherency.js: Added.
(pow42):
(build42AsDouble.opaqueAdd):
(build42AsDouble):
(powDouble42):
(clobber):
(pow42NoConstantFolding):
(powDouble42NoConstantFolding):
* tests/stress/pow-evaluation-order.js: Added.
(shouldBe):
(throw.new.Error):
* tests/stress/pow-expects-update-expression-on-lhs.js: Added.
(testSyntax):
(testSyntaxError):
(throw.new.Error):
(let.token.of.tokens.testSyntax.pow):
(testSyntax.pow):
* tests/stress/pow-integer-exponent-fastpath.js: Added.
(valuesAreClose):
(mathPowDoubleDoubleTestExponentFifty):
(mathPowDoubleIntTestExponentFifty):
(testExponentFifty):
(mathPowDoubleDoubleTestExponentTenThousands):
(mathPowDoubleIntTestExponentTenThousands):
(testExponentTenThousands):
* tests/stress/pow-nan-behaviors.js: Added.
(testIntegerBaseWithNaNExponentStatic):
(mathPowIntegerBaseWithNaNExponentDynamic):
(testIntegerBaseWithNaNExponentDynamic):
(testFloatingPointBaseWithNaNExponentStatic):
(mathPowFloatingPointBaseWithNaNExponentDynamic):
(testFloatingPointBaseWithNaNExponentDynamic):
(testNaNBaseStatic):
(mathPowNaNBaseDynamic1):
(mathPowNaNBaseDynamic2):
(mathPowNaNBaseDynamic3):
(mathPowNaNBaseDynamic4):
(testNaNBaseDynamic):
(infiniteExponentsStatic):
(mathPowInfiniteExponentsDynamic1):
(mathPowInfiniteExponentsDynamic2):
(mathPowInfiniteExponentsDynamic3):
(mathPowInfiniteExponentsDynamic4):
(infiniteExponentsDynamic):
* tests/stress/pow-simple.js: Added.
(shouldBe):
(throw.new.Error):
* tests/stress/pow-stable-results.js: Added.
(opaquePow):
(isIdentical):
* tests/stress/pow-to-number-should-be-executed-in-code-side.js: Added.
(shouldBe):
(throw.new.Error):
* tests/stress/pow-with-constants.js: Added.
(exponentIsZero):
(testExponentIsZero):
(exponentIsOne):
(testExponentIsOne):
(powUsedAsSqrt):
(testPowUsedAsSqrt):
(powUsedAsOneOverSqrt):
(testPowUsedAsOneOverSqrt):
(powUsedAsSquare):
(testPowUsedAsSquare):
(intIntConstantsSmallNumbers):
(intIntConstantsLargeNumbers):
(intIntSmallConstants):
(intDoubleConstants):
(doubleDoubleConstants):
(doubleIntConstants):
(testBaseAndExponentConstantLiterals):
(exponentIsIntegerConstant):
(testExponentIsIntegerConstant):
(exponentIsDoubleConstant):
(testExponentIsDoubleConstant):
(exponentIsInfinityConstant):
(testExponentIsInfinityConstant):
(exponentIsNegativeInfinityConstant):
(testExponentIsNegativeInfinityConstant):
* tests/stress/pow-with-never-NaN-exponent.js: Added.
(exponentIsNonNanDouble1):
(exponentIsNonNanDouble2):
(testExponentIsDoubleConstant):
* tests/test262.yaml:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@203499 268f45cc-cd09-0410-ab3c-d52691b4dbfc

34 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITArithmetic.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/tests/stress/math-pow-basics.js
Source/JavaScriptCore/tests/stress/math-pow-nan-behaviors.js
Source/JavaScriptCore/tests/stress/pow-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-coherency.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-evaluation-order.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-expects-update-expression-on-lhs.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-integer-exponent-fastpath.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-nan-behaviors.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-simple.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-stable-results.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-to-number-should-be-executed-in-code-side.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-with-constants.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/pow-with-never-NaN-exponent.js [new file with mode: 0644]
Source/JavaScriptCore/tests/test262.yaml

index 8ea4629..518993a 100644 (file)
@@ -1,3 +1,228 @@
+2016-07-20  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES7] Introduce exponentiation expression
+        https://bugs.webkit.org/show_bug.cgi?id=159969
+
+        Reviewed by Saam Barati.
+
+        This patch implements the exponentiation expression, e.g. `x ** y`.
+        The exponentiation expression is introduced in ECMA262 2016 and ECMA262 2016
+        is already released. So this is not the draft spec.
+
+        The exponentiation expression has 2 interesting points.
+
+        1. Right associative
+
+            To follow the Math expression, ** operator is right associative.
+            When we execute `x ** y ** z`, this is handled as `x ** (y ** z)`, not `(x ** y) ** z`.
+            This patch introduces the right associativity to the binary operator and handles it
+            in the operator precedence parser in Parser.cpp.
+
+        2. LHS of the exponentiation expression is UpdateExpression
+
+            ExponentiationExpression[Yield]:
+                UnaryExpression[?Yield]
+                UpdateExpression[?Yield] ** ExponentiationExpression[?Yield]
+
+            As we can see, the left hand side of the ExponentiationExpression is UpdateExpression, not UnaryExpression.
+            It means that `+x ** y` becomes a syntax error. This is intentional. Without superscript in JS,
+            `-x**y` is confusing between `-(x ** y)` and `(-x) ** y`. So ECMA262 intentionally avoids UnaryExpression here.
+            If we need to use a negated value, we need to write parentheses explicitly e.g. `(-x) ** y`.
+            In this patch, we ensure that the left hand side is not an unary expression by checking an operator in
+            parseBinaryExpression. This works since `**` has the highest operator precedence in the binary operators.
+
+        We introduce a new bytecode, op_pow. That simply works as similar as the other binary operators.
+        And it is converted to ArithPow in DFG and handled in DFG and FTL.
+        In this patch, we take the approach just introducing a new bytecode instead of calling Math.pow.
+        This is because we would like to execute ToNumber in the caller side, not in the callee (Math.pow) side.
+        And we don't want to compile ** into the following.
+
+            lhsNumber = to_number (lhs)
+            rhsNumber = to_number (rhs)
+            call Math.pow(lhsNumber, rhsNumber)
+
+        We ensure that this patch passes all the test262 tests related to the exponentiation expression.
+
+        The only sensitive part to the performance is the parser changes.
+        So we measured the code-load performance and it is neutral in my x64 Linux box (hanayamata).
+
+            Collected 30 samples per benchmark/VM, with 30 VM invocations per benchmark. Emitted a call to
+            gc() between sample measurements. Used 1 benchmark iteration per VM invocation for warm-up. Used
+            the jsc-specific preciseTime() function to get microsecond-level timing. Reporting benchmark
+            execution times with 95% confidence intervals in milliseconds.
+
+                                     baseline                  patched
+
+            closure              0.60499+-0.00250          0.60180+-0.00244
+            jquery               7.89175+-0.02433    ?     7.91287+-0.04759       ?
+
+            <geometric>          2.18499+-0.00523          2.18207+-0.00689         might be 1.0013x faster
+
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::emitReadModifyAssignment):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_pow):
+        * llint/LowLevelInterpreter.asm:
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::operatorStackShouldReduce):
+        (JSC::ASTBuilder::makePowNode):
+        (JSC::ASTBuilder::makeMultNode):
+        (JSC::ASTBuilder::makeDivNode):
+        (JSC::ASTBuilder::makeModNode):
+        (JSC::ASTBuilder::makeSubNode):
+        (JSC::ASTBuilder::makeBinaryNode):
+        (JSC::ASTBuilder::operatorStackHasHigherPrecedence): Deleted.
+        * parser/Lexer.cpp:
+        (JSC::Lexer<T>::lex):
+        * parser/NodeConstructors.h:
+        (JSC::PowNode::PowNode):
+        * parser/Nodes.h:
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseAssignmentExpression):
+        (JSC::isUnaryOpExcludingUpdateOp):
+        (JSC::Parser<LexerType>::parseBinaryExpression):
+        (JSC::isUnaryOp): Deleted.
+        * parser/ParserTokens.h:
+        (JSC::isUpdateOp):
+        (JSC::isUnaryOp):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::operatorStackPop):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/CommonSlowPaths.h:
+        * tests/stress/pow-basics.js: Added.
+        (valuesAreClose):
+        (mathPowDoubleDouble1):
+        (mathPowDoubleInt1):
+        (test1):
+        (mathPowDoubleDouble2):
+        (mathPowDoubleInt2):
+        (test2):
+        (mathPowDoubleDouble3):
+        (mathPowDoubleInt3):
+        (test3):
+        (mathPowDoubleDouble4):
+        (mathPowDoubleInt4):
+        (test4):
+        (mathPowDoubleDouble5):
+        (mathPowDoubleInt5):
+        (test5):
+        (mathPowDoubleDouble6):
+        (mathPowDoubleInt6):
+        (test6):
+        (mathPowDoubleDouble7):
+        (mathPowDoubleInt7):
+        (test7):
+        (mathPowDoubleDouble8):
+        (mathPowDoubleInt8):
+        (test8):
+        (mathPowDoubleDouble9):
+        (mathPowDoubleInt9):
+        (test9):
+        (mathPowDoubleDouble10):
+        (mathPowDoubleInt10):
+        (test10):
+        (mathPowDoubleDouble11):
+        (mathPowDoubleInt11):
+        (test11):
+        * tests/stress/pow-coherency.js: Added.
+        (pow42):
+        (build42AsDouble.opaqueAdd):
+        (build42AsDouble):
+        (powDouble42):
+        (clobber):
+        (pow42NoConstantFolding):
+        (powDouble42NoConstantFolding):
+        * tests/stress/pow-evaluation-order.js: Added.
+        (shouldBe):
+        (throw.new.Error):
+        * tests/stress/pow-expects-update-expression-on-lhs.js: Added.
+        (testSyntax):
+        (testSyntaxError):
+        (throw.new.Error):
+        (let.token.of.tokens.testSyntax.pow):
+        (testSyntax.pow):
+        * tests/stress/pow-integer-exponent-fastpath.js: Added.
+        (valuesAreClose):
+        (mathPowDoubleDoubleTestExponentFifty):
+        (mathPowDoubleIntTestExponentFifty):
+        (testExponentFifty):
+        (mathPowDoubleDoubleTestExponentTenThousands):
+        (mathPowDoubleIntTestExponentTenThousands):
+        (testExponentTenThousands):
+        * tests/stress/pow-nan-behaviors.js: Added.
+        (testIntegerBaseWithNaNExponentStatic):
+        (mathPowIntegerBaseWithNaNExponentDynamic):
+        (testIntegerBaseWithNaNExponentDynamic):
+        (testFloatingPointBaseWithNaNExponentStatic):
+        (mathPowFloatingPointBaseWithNaNExponentDynamic):
+        (testFloatingPointBaseWithNaNExponentDynamic):
+        (testNaNBaseStatic):
+        (mathPowNaNBaseDynamic1):
+        (mathPowNaNBaseDynamic2):
+        (mathPowNaNBaseDynamic3):
+        (mathPowNaNBaseDynamic4):
+        (testNaNBaseDynamic):
+        (infiniteExponentsStatic):
+        (mathPowInfiniteExponentsDynamic1):
+        (mathPowInfiniteExponentsDynamic2):
+        (mathPowInfiniteExponentsDynamic3):
+        (mathPowInfiniteExponentsDynamic4):
+        (infiniteExponentsDynamic):
+        * tests/stress/pow-simple.js: Added.
+        (shouldBe):
+        (throw.new.Error):
+        * tests/stress/pow-stable-results.js: Added.
+        (opaquePow):
+        (isIdentical):
+        * tests/stress/pow-to-number-should-be-executed-in-code-side.js: Added.
+        (shouldBe):
+        (throw.new.Error):
+        * tests/stress/pow-with-constants.js: Added.
+        (exponentIsZero):
+        (testExponentIsZero):
+        (exponentIsOne):
+        (testExponentIsOne):
+        (powUsedAsSqrt):
+        (testPowUsedAsSqrt):
+        (powUsedAsOneOverSqrt):
+        (testPowUsedAsOneOverSqrt):
+        (powUsedAsSquare):
+        (testPowUsedAsSquare):
+        (intIntConstantsSmallNumbers):
+        (intIntConstantsLargeNumbers):
+        (intIntSmallConstants):
+        (intDoubleConstants):
+        (doubleDoubleConstants):
+        (doubleIntConstants):
+        (testBaseAndExponentConstantLiterals):
+        (exponentIsIntegerConstant):
+        (testExponentIsIntegerConstant):
+        (exponentIsDoubleConstant):
+        (testExponentIsDoubleConstant):
+        (exponentIsInfinityConstant):
+        (testExponentIsInfinityConstant):
+        (exponentIsNegativeInfinityConstant):
+        (testExponentIsNegativeInfinityConstant):
+        * tests/stress/pow-with-never-NaN-exponent.js: Added.
+        (exponentIsNonNanDouble1):
+        (exponentIsNonNanDouble2):
+        (testExponentIsDoubleConstant):
+        * tests/test262.yaml:
+
 2016-07-18  Filip Pizlo  <fpizlo@apple.com>
 
         Switching on symbols should be fast
index 5df99d9..13e982c 100644 (file)
@@ -39,6 +39,7 @@
             { "name" : "op_div", "length" : 5 },
             { "name" : "op_mod", "length" : 4 },
             { "name" : "op_sub", "length" : 5 },
+            { "name" : "op_pow", "length" : 4 },
             { "name" : "op_lshift", "length" : 4 },
             { "name" : "op_rshift", "length" : 4 },
             { "name" : "op_urshift", "length" : 4 },
index 3a2bb05..570844c 100644 (file)
@@ -208,6 +208,7 @@ void computeUsesForBytecodeOffset(
     case op_div:
     case op_mod:
     case op_sub:
+    case op_pow:
     case op_lshift:
     case op_rshift:
     case op_urshift:
@@ -431,6 +432,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     case op_div:
     case op_mod:
     case op_sub:
+    case op_pow:
     case op_lshift:
     case op_rshift:
     case op_urshift:
index 2df9eb5..3f7d2f0 100644 (file)
@@ -1024,6 +1024,10 @@ void CodeBlock::dumpBytecode(
             printBinaryOp(out, exec, location, it, "mod");
             break;
         }
+        case op_pow: {
+            printBinaryOp(out, exec, location, it, "pow");
+            break;
+        }
         case op_sub: {
             printBinaryOp(out, exec, location, it, "sub");
             ++it;
index d07eaca..5c8f93f 100644 (file)
@@ -1993,6 +1993,9 @@ static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(BytecodeGenerator& gen
         case OpModEq:
             opcodeID = op_mod;
             break;
+        case OpPowEq:
+            opcodeID = op_pow;
+            break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
             return dst;
index 18b4c32..bcf3692 100644 (file)
@@ -3819,6 +3819,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_mod);
         }
 
+        case op_pow: {
+            // FIXME: ArithPow(Untyped, Untyped) should be supported as the same to ArithMul, ArithSub etc.
+            // https://bugs.webkit.org/show_bug.cgi?id=160012
+            Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ArithPow, op1, op2));
+            NEXT_OPCODE(op_pow);
+        }
+
         case op_div: {
             Node* op1 = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* op2 = get(VirtualRegister(currentInstruction[3].u.operand));
index b01b070..bc511c5 100644 (file)
@@ -123,6 +123,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_negate:
     case op_mul:
     case op_mod:
+    case op_pow:
     case op_div:
     case op_debug:
     case op_profile_type:
index f27d39a..8fd3f58 100644 (file)
@@ -300,6 +300,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_nstricteq)
         DEFINE_OP(op_dec)
         DEFINE_OP(op_inc)
+        DEFINE_OP(op_pow)
         DEFINE_OP(op_profile_type)
         DEFINE_OP(op_profile_control_flow)
         DEFINE_OP(op_push_with_scope)
index 7311dfa..093d7da 100644 (file)
@@ -566,6 +566,7 @@ namespace JSC {
         void emit_op_nstricteq(Instruction*);
         void emit_op_dec(Instruction*);
         void emit_op_inc(Instruction*);
+        void emit_op_pow(Instruction*);
         void emit_op_profile_type(Instruction*);
         void emit_op_profile_control_flow(Instruction*);
         void emit_op_push_with_scope(Instruction*);
index 3038ff7..4855f1d 100644 (file)
@@ -1032,7 +1032,13 @@ void JIT::emitSlow_op_sub(Instruction* currentInstruction, Vector<SlowCaseEntry>
     }
 }
 
-/* ------------------------------ END: OP_ADD, OP_SUB, OP_MUL ------------------------------ */
+void JIT::emit_op_pow(Instruction* currentInstruction)
+{
+    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_pow);
+    slowPathCall.call();
+}
+
+/* ------------------------------ END: OP_ADD, OP_SUB, OP_MUL, OP_POW ------------------------------ */
 
 } // namespace JSC
 
index 0199315..3e0dbd8 100644 (file)
@@ -1320,6 +1320,12 @@ _llint_op_mod:
     dispatch(4)
 
 
+_llint_op_pow:
+    traceExecution()
+    callOpcodeSlowPath(_slow_path_pow)
+    dispatch(4)
+
+
 _llint_op_typeof:
     traceExecution()
     callOpcodeSlowPath(_slow_path_typeof)
index 9ac904a..19e362e 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "BuiltinNames.h"
 #include "BytecodeIntrinsicRegistry.h"
+#include "MathCommon.h"
 #include "NodeConstructors.h"
 #include "SyntaxChecker.h"
 #include "VariableEnvironment.h"
@@ -143,6 +144,7 @@ public:
     ExpressionNode* makeDeleteNode(const JSTokenLocation&, ExpressionNode*, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end);
     ExpressionNode* makeNegateNode(const JSTokenLocation&, ExpressionNode*);
     ExpressionNode* makeBitwiseNotNode(const JSTokenLocation&, ExpressionNode*);
+    ExpressionNode* makePowNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
     ExpressionNode* makeMultNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
     ExpressionNode* makeDivNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
     ExpressionNode* makeModNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments);
@@ -784,9 +786,14 @@ public:
         operatorStackDepth--;
         m_binaryOperatorStack.removeLast();
     }
-    bool operatorStackHasHigherPrecedence(int&, int precedence)
+    bool operatorStackShouldReduce(int precedence)
     {
-        return precedence <= m_binaryOperatorStack.last().second;
+        // If the current precedence of the operator stack is the same to the one of the given operator,
+        // it depends on the associative whether we reduce the stack.
+        // If the operator is right associative, we should not reduce the stack right now.
+        if (precedence == m_binaryOperatorStack.last().second)
+            return !(m_binaryOperatorStack.last().first & RightAssociativeBinaryOpTokenFlag);
+        return precedence < m_binaryOperatorStack.last().second;
     }
     const BinaryOperand& getFromOperandStack(int i) { return m_binaryOperandStack[m_binaryOperandStack.size() + i]; }
     void shrinkOperandStackBy(int& operandStackDepth, int amount)
@@ -1044,8 +1051,29 @@ ExpressionNode* ASTBuilder::makeBitwiseNotNode(const JSTokenLocation& location,
     return new (m_parserArena) BitwiseNotNode(location, expr);
 }
 
+ExpressionNode* ASTBuilder::makePowNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
+{
+    auto* strippedExpr1 = expr1->stripUnaryPlus();
+    auto* strippedExpr2 = expr2->stripUnaryPlus();
+
+    if (strippedExpr1->isNumber() && strippedExpr2->isNumber()) {
+        const NumberNode& numberExpr1 = static_cast<NumberNode&>(*strippedExpr1);
+        const NumberNode& numberExpr2 = static_cast<NumberNode&>(*strippedExpr2);
+        return createNumberFromBinaryOperation(location, operationMathPow(numberExpr1.value(), numberExpr2.value()), numberExpr1, numberExpr2);
+    }
+
+    if (strippedExpr1->isNumber())
+        expr1 = strippedExpr1;
+    if (strippedExpr2->isNumber())
+        expr2 = strippedExpr2;
+
+    return new (m_parserArena) PowNode(location, expr1, expr2, rightHasAssignments);
+}
+
 ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
+    // FIXME: Unary + change the evaluation order.
+    // https://bugs.webkit.org/show_bug.cgi?id=159968
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
@@ -1066,6 +1094,8 @@ ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, Expres
 
 ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
+    // FIXME: Unary + change the evaluation order.
+    // https://bugs.webkit.org/show_bug.cgi?id=159968
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
@@ -1082,6 +1112,8 @@ ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, Express
 
 ExpressionNode* ASTBuilder::makeModNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
+    // FIXME: Unary + change the evaluation order.
+    // https://bugs.webkit.org/show_bug.cgi?id=159968
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
@@ -1106,6 +1138,8 @@ ExpressionNode* ASTBuilder::makeAddNode(const JSTokenLocation& location, Express
 
 ExpressionNode* ASTBuilder::makeSubNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
 {
+    // FIXME: Unary + change the evaluation order.
+    // https://bugs.webkit.org/show_bug.cgi?id=159968
     expr1 = expr1->stripUnaryPlus();
     expr2 = expr2->stripUnaryPlus();
 
@@ -1295,6 +1329,9 @@ ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int
 
     case MOD:
         return makeModNode(location, lhs.first, rhs.first, rhs.second.hasAssignment);
+
+    case POW:
+        return makePowNode(location, lhs.first, rhs.first, rhs.second.hasAssignment);
     }
     CRASH();
     return 0;
index 5c79673..b726dda 100644 (file)
@@ -1956,6 +1956,16 @@ start:
             token = MULTEQUAL;
             break;
         }
+        if (m_current == '*') {
+            shift();
+            if (m_current == '=') {
+                shift();
+                token = POWEQUAL;
+                break;
+            }
+            token = POW;
+            break;
+        }
         token = TIMES;
         break;
     case CharacterSlash:
index 7f546ab..5d3f3c9 100644 (file)
@@ -497,6 +497,11 @@ namespace JSC {
     {
     }
 
+    inline PowNode::PowNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
+        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_pow, rightHasAssignments)
+    {
+    }
+
     inline MultNode::MultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
         : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mul, rightHasAssignments)
     {
index 1dc74e2..1e08827 100644 (file)
@@ -67,6 +67,7 @@ namespace JSC {
         OpXOrEq,
         OpOrEq,
         OpModEq,
+        OpPowEq,
         OpLShift,
         OpRShift,
         OpURShift
@@ -1039,6 +1040,11 @@ namespace JSC {
         bool m_rightHasAssignments;
     };
 
+    class PowNode : public BinaryOpNode {
+    public:
+        PowNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments);
+    };
+
     class MultNode : public BinaryOpNode {
     public:
         MultNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments);
index 4eeae14..fe54952 100644 (file)
@@ -3126,6 +3126,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
         case XOREQUAL: op = OpXOrEq; break;
         case OREQUAL: op = OpOrEq; break;
         case MODEQUAL: op = OpModEq; break;
+        case POWEQUAL: op = OpPowEq; break;
         default:
             goto end;
         }
@@ -3218,9 +3219,11 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseConditionalE
     return context.createConditionalExpr(location, cond, lhs, rhs);
 }
 
-ALWAYS_INLINE static bool isUnaryOp(JSTokenType token)
+ALWAYS_INLINE static bool isUnaryOpExcludingUpdateOp(JSTokenType token)
 {
-    return token & UnaryOpTokenFlag;
+    if (isUpdateOp(token))
+        return false;
+    return isUnaryOp(token);
 }
 
 template <typename LexerType>
@@ -3241,10 +3244,35 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseBinaryExpres
     while (true) {
         JSTextPosition exprStart = tokenStartPosition();
         int initialAssignments = m_parserState.assignmentCount;
+        JSTokenType leadingTokenTypeForUnaryExpression = m_token.m_type;
         TreeExpression current = parseUnaryExpression(context);
         failIfFalse(current, "Cannot parse expression");
         
         context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_parserState.assignmentCount);
+
+        // 12.6 https://tc39.github.io/ecma262/#sec-exp-operator
+        // ExponentiationExpresion is described as follows.
+        //
+        //     ExponentiationExpression[Yield]:
+        //         UnaryExpression[?Yield]
+        //         UpdateExpression[?Yield] ** ExponentiationExpression[?Yield]
+        //
+        // As we can see, the left hand side of the ExponentiationExpression is UpdateExpression, not UnaryExpression.
+        // So placing UnaryExpression not included in UpdateExpression here is a syntax error.
+        // This is intentional. For example, if UnaryExpression is allowed, we can have the code like `-x**y`.
+        // But this is confusing: `-(x**y)` OR `(-x)**y`, which interpretation is correct?
+        // To avoid this problem, ECMA262 makes unparenthesized exponentiation expression as operand of unary operators an early error.
+        // More rationale: https://mail.mozilla.org/pipermail/es-discuss/2015-September/044232.html
+        //
+        // Here, we guarantee that the left hand side of this expression is not unary expression by checking the leading operator of the parseUnaryExpression.
+        // This check just works. Let's consider the example,
+        //     y <> -x ** z
+        //          ^
+        //          Check this.
+        // If the binary operator <> has higher precedence than one of "**", this check does not work.
+        // But it's OK for ** because the operator "**" has the highest operator precedence in the binary operators.
+        failIfTrue(match(POW) && isUnaryOpExcludingUpdateOp(leadingTokenTypeForUnaryExpression), "Unparenthesized unary expression found on the left hand side of an exponentiation expression");
+
         int precedence = isBinaryOperator(m_token.m_type);
         if (!precedence)
             break;
@@ -3253,7 +3281,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseBinaryExpres
         int operatorToken = m_token.m_type;
         next(TreeBuilder::DontBuildStrings);
         
-        while (operatorStackDepth &&  context.operatorStackHasHigherPrecedence(operatorStackDepth, precedence)) {
+        while (operatorStackDepth && context.operatorStackShouldReduce(precedence)) {
             ASSERT(operandStackDepth > 1);
             
             typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1);
index aff4a30..eb263bb 100644 (file)
@@ -41,7 +41,8 @@ enum {
     BinaryOpTokenAllowsInPrecedenceAdditionalShift = 4,
     BinaryOpTokenPrecedenceMask = 15 << BinaryOpTokenPrecedenceShift,
     ErrorTokenFlag = 1 << (BinaryOpTokenAllowsInPrecedenceAdditionalShift + BinaryOpTokenPrecedenceShift + 7),
-    UnterminatedErrorTokenFlag = ErrorTokenFlag << 1
+    UnterminatedErrorTokenFlag = ErrorTokenFlag << 1,
+    RightAssociativeBinaryOpTokenFlag = UnterminatedErrorTokenFlag << 1
 };
 
 #define BINARY_OP_PRECEDENCE(prec) (((prec) << BinaryOpTokenPrecedenceShift) | ((prec) << (BinaryOpTokenPrecedenceShift + BinaryOpTokenAllowsInPrecedenceAdditionalShift)))
@@ -109,6 +110,7 @@ enum JSTokenType {
     URSHIFTEQUAL,
     ANDEQUAL,
     MODEQUAL,
+    POWEQUAL,
     XOREQUAL,
     OREQUAL,
     DOTDOTDOT,
@@ -118,10 +120,10 @@ enum JSTokenType {
     // Begin tagged tokens
     PLUSPLUS = 0 | UnaryOpTokenFlag,
     MINUSMINUS = 1 | UnaryOpTokenFlag,
-    EXCLAMATION = 2 | UnaryOpTokenFlag,
-    TILDE = 3 | UnaryOpTokenFlag,
-    AUTOPLUSPLUS = 4 | UnaryOpTokenFlag,
-    AUTOMINUSMINUS = 5 | UnaryOpTokenFlag,
+    AUTOPLUSPLUS = 2 | UnaryOpTokenFlag,
+    AUTOMINUSMINUS = 3 | UnaryOpTokenFlag,
+    EXCLAMATION = 4 | UnaryOpTokenFlag,
+    TILDE = 5 | UnaryOpTokenFlag,
     TYPEOF = 6 | UnaryOpTokenFlag | KeywordTokenFlag,
     VOIDTOKEN = 7 | UnaryOpTokenFlag | KeywordTokenFlag,
     DELETETOKEN = 8 | UnaryOpTokenFlag | KeywordTokenFlag,
@@ -148,6 +150,7 @@ enum JSTokenType {
     TIMES = 20 | BINARY_OP_PRECEDENCE(10),
     DIVIDE = 21 | BINARY_OP_PRECEDENCE(10),
     MOD = 22 | BINARY_OP_PRECEDENCE(10),
+    POW = 23 | BINARY_OP_PRECEDENCE(11) | RightAssociativeBinaryOpTokenFlag, // Make sure that POW has the highest operator precedence.
     ERRORTOK = 0 | ErrorTokenFlag,
     UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK = 0 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     INVALID_IDENTIFIER_ESCAPE_ERRORTOK = 1 | ErrorTokenFlag,
@@ -222,6 +225,16 @@ struct JSToken {
     JSTextPosition m_endPosition;
 };
 
+ALWAYS_INLINE bool isUpdateOp(JSTokenType token)
+{
+    return token >= PLUSPLUS && token <= AUTOMINUSMINUS;
+}
+
+ALWAYS_INLINE bool isUnaryOp(JSTokenType token)
+{
+    return token & UnaryOpTokenFlag;
+}
+
 } // namespace JSC
 
 #endif // ParserTokens_h
index bf0d8ff..03782ce 100644 (file)
@@ -301,7 +301,7 @@ public:
     
     // Logic to handle datastructures used during parsing of binary expressions
     void operatorStackPop(int& operatorStackDepth) { operatorStackDepth--; }
-    bool operatorStackHasHigherPrecedence(int&, int) { return true; }
+    bool operatorStackShouldReduce(int) { return true; }
     BinaryOperand getFromOperandStack(int) { return m_topBinaryExpr; }
     void shrinkOperandStackBy(int& operandStackDepth, int amount) { operandStackDepth -= amount; }
     void appendBinaryOperation(const JSTokenLocation&, int& operandStackDepth, int&, BinaryOperand, BinaryOperand) { operandStackDepth++; }
index 0061245..65fda68 100644 (file)
@@ -470,6 +470,14 @@ SLOW_PATH_DECL(slow_path_mod)
     RETURN(jsNumber(jsMod(a, b)));
 }
 
+SLOW_PATH_DECL(slow_path_pow)
+{
+    BEGIN();
+    double a = OP_C(2).jsValue().toNumber(exec);
+    double b = OP_C(3).jsValue().toNumber(exec);
+    RETURN(jsNumber(operationMathPow(a, b)));
+}
+
 SLOW_PATH_DECL(slow_path_lshift)
 {
     BEGIN();
index ebc9130..8c70784 100644 (file)
@@ -219,6 +219,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_mul);
 SLOW_PATH_HIDDEN_DECL(slow_path_sub);
 SLOW_PATH_HIDDEN_DECL(slow_path_div);
 SLOW_PATH_HIDDEN_DECL(slow_path_mod);
+SLOW_PATH_HIDDEN_DECL(slow_path_pow);
 SLOW_PATH_HIDDEN_DECL(slow_path_lshift);
 SLOW_PATH_HIDDEN_DECL(slow_path_rshift);
 SLOW_PATH_HIDDEN_DECL(slow_path_urshift);
index ddf1988..1f1ca1e 100644 (file)
@@ -181,7 +181,7 @@ function test7(x, y, expected1, expected2) {
     }
 }
 noInline(test7);
-test6(-37.676522764377296, -1.0, -0.026541727490454296, -0.026541727490454296);
+test7(-37.676522764377296, -1.0, -0.026541727490454296, -0.026541727490454296);
 
 // Let's square things.
 function mathPowDoubleDouble8(x, y) {
@@ -207,7 +207,7 @@ function test8(x, y, expected1, expected2) {
     }
 }
 noInline(test8);
-test7(-37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
+test8(-37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
 
 function mathPowDoubleDouble9(x, y) {
     return Math.pow(x, y)
@@ -232,7 +232,7 @@ function test9(x, y, expected1, expected2) {
     }
 }
 noInline(test9);
-test8(37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
+test9(37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
 
 // Let's cube things.
 function mathPowDoubleDouble10(x, y) {
@@ -257,8 +257,8 @@ function test10(x, y, expected1, expected2) {
             throw "Error: bad result, mathPowDoubleInt(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
     }
 }
-noInline(test9);
-test9(-37.676522764377296, 3.0, -53482.591444930236, -53482.591444930236);
+noInline(test10);
+test10(-37.676522764377296, 3.0, -53482.591444930236, -53482.591444930236);
 
 function mathPowDoubleDouble11(x, y) {
     return Math.pow(x, y)
@@ -282,5 +282,5 @@ function test11(x, y, expected1, expected2) {
             throw "Error: bad result, mathPowDoubleInt(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
     }
 }
-noInline(test10);
-test10(37.676522764377296, 3.0, 53482.591444930236, 53482.591444930236);
+noInline(test11);
+test11(37.676522764377296, 3.0, 53482.591444930236, 53482.591444930236);
index 0bdd698..d67a1f9 100644 (file)
@@ -1,4 +1,4 @@
-// If y is NaN, the result is NaN.
+// If an argument is NaN, the result of Math.pow(x, y) is NaN.
 function testIntegerBaseWithNaNExponentStatic() {
     for (var i = 0; i < 10000; ++i) {
         var result = Math.pow(5, NaN);
@@ -72,7 +72,7 @@ noInline(testFloatingPointBaseWithNaNExponentDynamic);
 testFloatingPointBaseWithNaNExponentDynamic();
 
 // If y is +0, the result is 1, even if x is NaN.
-// If y is 0, the result is 1, even if x is NaN.
+// If y is -0, the result is 1, even if x is NaN.
 // If x is NaN and y is nonzero, the result is NaN.
 function testNaNBaseStatic() {
     for (var i = 0; i < 10000; ++i) {
@@ -140,8 +140,8 @@ function testNaNBaseDynamic() {
 noInline(testNaNBaseDynamic);
 testNaNBaseDynamic();
 
-// If abs(x) is 1 and y is +∞, the result is NaN.
-// If abs(x) is 1 and y is −∞, the result is NaN.
+// If abs(x) is 1 and y is +Inf the result is NaN.
+// If abs(x) is 1 and y is −Inf the result is NaN.
 function infiniteExponentsStatic() {
     for (var i = 0; i < 10000; ++i) {
         var result = Math.pow(1, Number.POSITIVE_INFINITY);
@@ -206,4 +206,4 @@ function infiniteExponentsDynamic() {
     }
 }
 noInline(infiniteExponentsDynamic);
-infiniteExponentsDynamic();
\ No newline at end of file
+infiniteExponentsDynamic();
diff --git a/Source/JavaScriptCore/tests/stress/pow-basics.js b/Source/JavaScriptCore/tests/stress/pow-basics.js
new file mode 100644 (file)
index 0000000..8842d49
--- /dev/null
@@ -0,0 +1,286 @@
+function valuesAreClose(a, b) {
+    return Math.abs(a / b) - 1 < 1e-10;
+}
+
+// Some random values.
+function mathPowDoubleDouble1(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble1);
+
+function mathPowDoubleInt1(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt1);
+
+function test1(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble1(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble1(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt1(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt1(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test1);
+test1(376.76522764377296, 10.81699226051569, 7.333951929109252e+27, 5.76378989575089e+25);
+
+function mathPowDoubleDouble2(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble2);
+
+function mathPowDoubleInt2(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt2);
+function test2(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble2(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble2(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt2(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt2(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test2);
+test2(376.76522764377296, -5.81699226051569, 1.035180331187579e-15, 1.3171824310400265e-13);
+
+function mathPowDoubleDouble3(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble3);
+
+function mathPowDoubleInt3(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt3);
+function test3(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble3(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble3(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt3(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt3(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test3);
+test3(-37.676522764377296, 10.0, 5763789895750892, 5763789895750892);
+
+// Exponent zero.
+function mathPowDoubleDouble4(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble4);
+
+function mathPowDoubleInt4(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt4);
+function test4(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble4(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble4(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt4(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt4(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test4);
+test4(-37.676522764377296, 0, 1, 1);
+
+// Exponent minus zero.
+function mathPowDoubleDouble5(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble5);
+
+function mathPowDoubleInt5(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt5);
+function test5(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble5(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble5(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt5(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test5);
+test5(-37.676522764377296, -0, 1, 1);
+
+// Exponent 1.
+function mathPowDoubleDouble6(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble6);
+
+function mathPowDoubleInt6(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt6);
+function test6(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble6(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble6(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt6(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt6(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test6);
+test6(-37.676522764377296, 1.0, -37.676522764377296, -37.676522764377296);
+
+// Exponent -1.
+function mathPowDoubleDouble7(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble7);
+
+function mathPowDoubleInt7(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt7);
+function test7(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble7(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble7(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble7(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleDouble7(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test7);
+test7(-37.676522764377296, -1.0, -0.026541727490454296, -0.026541727490454296);
+
+// Let's square things.
+function mathPowDoubleDouble8(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble8);
+
+function mathPowDoubleInt8(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt8);
+function test8(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble8(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble8(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt8(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt8(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test8);
+test8(-37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
+
+function mathPowDoubleDouble9(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble9);
+
+function mathPowDoubleInt9(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt9);
+function test9(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble9(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble9(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt9(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt9(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test9);
+test9(37.676522764377296, 2.0, 1419.5203676146407, 1419.5203676146407);
+
+// Let's cube things.
+function mathPowDoubleDouble10(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble10);
+
+function mathPowDoubleInt10(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt10);
+function test10(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble10(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt10(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test10);
+test10(-37.676522764377296, 3.0, -53482.591444930236, -53482.591444930236);
+
+function mathPowDoubleDouble11(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleDouble11);
+
+function mathPowDoubleInt11(x, y) {
+    return x ** y;
+}
+noInline(mathPowDoubleInt11);
+function test11(x, y, expected1, expected2) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDouble11(x, y);
+        if (!valuesAreClose(result, expected1))
+            throw "Error: bad result, mathPowDoubleDouble(" + x + ", " + y + ") = " + result + " expected a value close to " + expected1;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleInt11(x, integerY);
+        if (!valuesAreClose(result, expected2))
+            throw "Error: bad result, mathPowDoubleInt(" + x + ", " + integerY + ") = " + result + " expected a value close to " + expected2;
+    }
+}
+noInline(test11);
+test11(37.676522764377296, 3.0, 53482.591444930236, 53482.591444930236);
diff --git a/Source/JavaScriptCore/tests/stress/pow-coherency.js b/Source/JavaScriptCore/tests/stress/pow-coherency.js
new file mode 100644 (file)
index 0000000..29671a4
--- /dev/null
@@ -0,0 +1,95 @@
+//@ skip
+
+// This test checks that the pow function returns coherent results:
+// (a) Across different compilation tiers
+// (b) With integer exponents represented as int32 or as double
+
+function pow42() {
+    return { value: 2.1 ** 42, ftl: isFinalTier() };
+}
+
+function build42AsDouble() {
+    function opaqueAdd(x, y) { return x + y; }
+    return opaqueAdd(42 - 0.123, 0.123);
+}
+
+var double42 = build42AsDouble();
+
+if (double42 !== 42)
+    throw new Error("42 (as double) should be === to 42 (as int)");
+
+function powDouble42() {
+    return { value: 2.1 ** double42, ftl: isFinalTier() };
+}
+
+function clobber() { }
+noInline(clobber);
+
+function pow42NoConstantFolding() {
+    var obj = { x: 2.1, y: 42 };
+    clobber(obj);
+    return { value: obj.x ** obj.y, ftl: isFinalTier() };
+}
+
+function powDouble42NoConstantFolding() {
+    var obj = { x: 2.1, y: double42 };
+    clobber(obj);
+    return { value: obj.x ** obj.y, ftl: isFinalTier() };
+}
+
+var results = { 'jit': {}, 'dfg': {}, 'ftl': {} };
+var funs = [
+    [ 'pow42', pow42 ],
+    [ 'powDouble42', powDouble42 ],
+    [ 'pow42NoConstantFolding', pow42NoConstantFolding ],
+    [ 'powDouble42NoConstantFolding', powDouble42NoConstantFolding ]
+];
+var tiers = ['jit', 'dfg', 'ftl'];
+
+for (var i = 0; i < 100000; ++i) {
+    for (var j in funs) {
+        var name = funs[j][0];
+        var fun = funs[j][1];
+        var result = fun();
+        if (result.ftl)
+            results['ftl'][name] = result.value;
+        else if (numberOfDFGCompiles(fun) > 0)
+            results['dfg'][name] = result.value;
+        else
+            results['jit'][name] = result.value;
+    }
+}
+
+var errors = [];
+var valuesFor = {};
+for (var i in tiers) {
+    var tier = tiers[i];
+    var result = results[tier];
+    // We don't have this tier
+    if (Object.keys(result).length === 0)
+        continue;
+
+    for (var j in funs) {
+        var name = funs[j][0];
+        if (!(name in result))
+            errors.push(name + " was not compiled to " + tier);
+        else if (!(name in valuesFor))
+            valuesFor[name] = { value: result[name], tiers: [tier] };
+        else if (result[name] !== valuesFor[name].value)
+            errors.push(name + " has different results in " + tier + " (" + result[name] + ") and " + valuesFor[name].tiers + " (" + valuesFor[name].value + ")");
+        else
+            valuesFor[name].tiers.push(tier);
+    }
+}
+
+var reference = funs[0][0];
+var result = valuesFor[reference].value;
+
+for (var j in funs) {
+    var name = funs[j][0];
+    if (valuesFor[name].value !== result)
+        errors.push(name + " (" + valuesFor[name].value + ") and " + reference + " (" + result + ") have different results");
+}
+
+if (errors.length > 0)
+    throw new Error(errors.join('\n'));
diff --git a/Source/JavaScriptCore/tests/stress/pow-evaluation-order.js b/Source/JavaScriptCore/tests/stress/pow-evaluation-order.js
new file mode 100644 (file)
index 0000000..7154647
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let capture = [];
+    let leftValue = { valueOf() { capture.push("leftValue"); return 3; }};
+    let rightValue = { valueOf() { capture.push("rightValue"); return 2; }};
+
+    (capture.push("left"), leftValue) ** +(capture.push("right"), rightValue);
+//                                       ^
+//                                Changes the order
+
+    // Expected per operator evaluation order: "left", "right", "rightValue", "leftValue"
+    shouldBe(capture[0], "left");
+    shouldBe(capture[1], "right");
+    shouldBe(capture[2], "rightValue");
+    shouldBe(capture[3], "leftValue");
+}
+
+{
+    let capture = [];
+    let leftValue = { valueOf() { capture.push("leftValue"); return 3; }};
+    let rightValue = { valueOf() { capture.push("rightValue"); return 2; }};
+
+    (+(capture.push("left"), leftValue)) ** (capture.push("right"), rightValue);
+//   ^
+//   Changes the order
+
+    // Expected per operator evaluation order: "left", "right", "rightValue", "leftValue"
+    shouldBe(capture[0], "left");
+    shouldBe(capture[1], "leftValue");
+    shouldBe(capture[2], "right");
+    shouldBe(capture[3], "rightValue");
+}
diff --git a/Source/JavaScriptCore/tests/stress/pow-expects-update-expression-on-lhs.js b/Source/JavaScriptCore/tests/stress/pow-expects-update-expression-on-lhs.js
new file mode 100644 (file)
index 0000000..6408151
--- /dev/null
@@ -0,0 +1,96 @@
+function testSyntax(script) {
+    try {
+        eval(script);
+    } catch (error) {
+        if (error instanceof SyntaxError)
+            throw new Error("Bad error: " + String(error));
+    }
+}
+
+function testSyntaxError(script, message) {
+    var error = null;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("Expected syntax error not thrown");
+
+    if (String(error) !== message)
+        throw new Error("Bad error: " + String(error));
+}
+
+{
+    let tokens = [
+        '-',
+        '+',
+        '~',
+        '!',
+        'typeof',
+        'void',
+        'delete',
+    ];
+
+    for (let token of tokens) {
+        testSyntaxError(`
+        function pow(a, b)
+        {
+            return ${token} a ** b;
+        }
+        `, `SyntaxError: Unexpected token '**'. Unparenthesized unary expression found on the left hand side of an exponentiation expression.`);
+    }
+}
+
+{
+    let tokens = [
+        '-',
+        '+',
+        '~',
+        '!',
+        'typeof',
+        'void',
+        'delete',
+    ];
+
+    for (let token of tokens) {
+        testSyntax(`
+        function pow(a, b)
+        {
+            return (${token} a) ** b;
+        }
+        `);
+    }
+}
+
+{
+    let tokens = [
+        '++',
+        '--',
+    ];
+
+    for (let token of tokens) {
+        testSyntax(`
+        function pow(a, b)
+        {
+            return ${token} a ** b;
+        }
+        `);
+    }
+}
+
+{
+    let tokens = [
+        '++',
+        '--',
+    ];
+
+    for (let token of tokens) {
+        testSyntax(`
+        function pow(a, b)
+        {
+            return a ${token} ** b;
+        }
+        `);
+    }
+}
diff --git a/Source/JavaScriptCore/tests/stress/pow-integer-exponent-fastpath.js b/Source/JavaScriptCore/tests/stress/pow-integer-exponent-fastpath.js
new file mode 100644 (file)
index 0000000..21ee27c
--- /dev/null
@@ -0,0 +1,56 @@
+function valuesAreClose(a, b) {
+    return Math.abs(a / b) - 1 < 1e-10;
+}
+
+// Small exponent values are handled through a simpler inline loop. Test that it is not observable.
+function mathPowDoubleDoubleTestExponentFifty(x, y) {
+    return x ** y
+}
+noInline(mathPowDoubleDoubleTestExponentFifty);
+
+function mathPowDoubleIntTestExponentFifty(x, y) {
+    return x ** y
+}
+noInline(mathPowDoubleIntTestExponentFifty);
+function testExponentFifty(x, y, expected) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDoubleTestExponentFifty(x, y);
+        if (!valuesAreClose(result, expected))
+            throw "Error: bad result, (" + x + ") ** (" + y + ") = " + result + " expected value close to " + expected;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleIntTestExponentFifty(x, integerY);
+        if (!valuesAreClose(result, expected))
+            throw "Error: bad result, (" + x + ") ** (" + integerY + ") = " + result + " expected value close to " + expected;
+    }
+}
+noInline(testExponentFifty);
+testExponentFifty(53.70901164133102, 50.0, 3.179494118120144e+86);
+testExponentFifty(53.70901164133102, -10.0, 5.006432842621192e-18);
+
+function mathPowDoubleDoubleTestExponentTenThousands(x, y) {
+    return x ** y
+}
+noInline(mathPowDoubleDoubleTestExponentTenThousands);
+
+function mathPowDoubleIntTestExponentTenThousands(x, y) {
+    return x ** y
+}
+noInline(mathPowDoubleIntTestExponentTenThousands);
+function testExponentTenThousands(x, y, expected) {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleDoubleTestExponentTenThousands(x, y);
+        if (!valuesAreClose(result, expected))
+            throw "Error: bad result, (" + x + ") ** (" + y + ") = " + result + " expected value close to " + expected;
+    }
+    var integerY = y | 0;
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowDoubleIntTestExponentTenThousands(x, integerY);
+        if (!valuesAreClose(result, expected))
+            throw "Error: bad result, (" + x + ") ** (" + integerY + ") = " + result + " expected value close to " + expected;
+    }
+}
+noInline(testExponentTenThousands);
+testExponentTenThousands(1.001, 10000.0, 21916.681339048373);
+testExponentTenThousands(1.001, -1.0, 0.9990009990009991);
diff --git a/Source/JavaScriptCore/tests/stress/pow-nan-behaviors.js b/Source/JavaScriptCore/tests/stress/pow-nan-behaviors.js
new file mode 100644 (file)
index 0000000..3d75482
--- /dev/null
@@ -0,0 +1,209 @@
+// If an argument is NaN, the result of x ** y is NaN.
+function testIntegerBaseWithNaNExponentStatic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = 5 ** NaN;
+        if (!isNaN(result))
+            throw "Error: bad result, 5 ** NaN = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = i ** NaN;
+        if (!isNaN(result))
+            throw "Error: bad result, i ** NaN = " + result + " with i = " + i;
+    }
+}
+noInline(testIntegerBaseWithNaNExponentStatic);
+testIntegerBaseWithNaNExponentStatic();
+
+function mathPowIntegerBaseWithNaNExponentDynamic(x, y) {
+    return x ** y;
+}
+noInline(mathPowIntegerBaseWithNaNExponentDynamic);
+function testIntegerBaseWithNaNExponentDynamic() {
+    // Warm up with 2 integers.
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowIntegerBaseWithNaNExponentDynamic(2, 5);
+        if (result !== 32)
+            throw "Error: bad result, mathPowIntegerBaseWithNaNExponentDynamic(2, 5) = " + result + ", expected 32."
+    }
+
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowIntegerBaseWithNaNExponentDynamic(i, NaN);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowIntegerBaseWithNaNExponentDynamic(i, NaN) = " + result + " with i = " + i + ", expected NaN";
+    }
+}
+noInline(testIntegerBaseWithNaNExponentDynamic);
+testIntegerBaseWithNaNExponentDynamic();
+
+function testFloatingPointBaseWithNaNExponentStatic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = 5.5 ** NaN;
+        if (!isNaN(result))
+            throw "Error: bad result, 5.5 ** NaN = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = (i + 0.5) ** NaN;
+        if (!isNaN(result))
+            throw "Error: bad result, (i + 0.5) ** NaN = " + result + " with i = " + i;
+    }
+}
+noInline(testFloatingPointBaseWithNaNExponentStatic);
+testFloatingPointBaseWithNaNExponentStatic();
+
+function mathPowFloatingPointBaseWithNaNExponentDynamic(x, y) {
+    return x ** y;
+}
+noInline(mathPowFloatingPointBaseWithNaNExponentDynamic);
+function testFloatingPointBaseWithNaNExponentDynamic() {
+    // Warm up with 2 double.
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowFloatingPointBaseWithNaNExponentDynamic(2.5, 5.1);
+        if (result !== 107.02717054543135)
+            throw "Error: bad result, mathPowFloatingPointBaseWithNaNExponentDynamic(2.5, 5.1) = " + result + ", expected 107.02717054543135."
+    }
+
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowFloatingPointBaseWithNaNExponentDynamic(i + 0.5, NaN);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowFloatingPointBaseWithNaNExponentDynamic(i + 0.5, NaN) = " + result + " with i = " + i + ", expected NaN";
+    }
+}
+noInline(testFloatingPointBaseWithNaNExponentDynamic);
+testFloatingPointBaseWithNaNExponentDynamic();
+
+// If y is +0, the result is 1, even if x is NaN.
+// If y is -0, the result is 1, even if x is NaN.
+// If x is NaN and y is nonzero, the result is NaN.
+function testNaNBaseStatic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = NaN ** (i + 1);
+        if (!isNaN(result))
+            throw "Error: bad result, NaN ** (i + 1) = " + result + " with i = " + i;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = NaN ** (i + 1.5);
+        if (!isNaN(result))
+            throw "Error: bad result, NaN ** (i + 1.5) = " + result + " with i = " + i;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = NaN ** 0;
+        if (result !== 1)
+            throw "Error: bad result, NaN ** 0 = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = NaN ** -0;
+        if (result !== 1)
+            throw "Error: bad result, NaN ** -0 = " + result;
+    }
+}
+noInline(testNaNBaseStatic);
+testNaNBaseStatic();
+
+function mathPowNaNBaseDynamic1(x, y) {
+    return x ** y;
+}
+function mathPowNaNBaseDynamic2(x, y) {
+    return x ** y;
+}
+function mathPowNaNBaseDynamic3(x, y) {
+    return x ** y;
+}
+function mathPowNaNBaseDynamic4(x, y) {
+    return x ** y;
+}
+noInline(mathPowNaNBaseDynamic1);
+noInline(mathPowNaNBaseDynamic2);
+noInline(mathPowNaNBaseDynamic3);
+noInline(mathPowNaNBaseDynamic4);
+function testNaNBaseDynamic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowNaNBaseDynamic1(NaN, i + 1);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowNaNBaseDynamic1(NaN, i + 1) = " + result + " with i = " + i;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowNaNBaseDynamic2(NaN, i + 1.5);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowNaNBaseDynamic2(NaN, i + 1.5) = " + result + " with i = " + i;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowNaNBaseDynamic3(NaN, 0);
+        if (result !== 1)
+            throw "Error: bad result, mathPowNaNBaseDynamic3(NaN, 0) = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowNaNBaseDynamic4(NaN, -0);
+        if (result !== 1)
+            throw "Error: bad result, mathPowNaNBaseDynamic4(NaN, -0) = " + result;
+    }
+}
+noInline(testNaNBaseDynamic);
+testNaNBaseDynamic();
+
+// If abs(x) is 1 and y is +Inf the result is NaN.
+// If abs(x) is 1 and y is −Inf the result is NaN.
+function infiniteExponentsStatic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = 1 ** Number.POSITIVE_INFINITY;
+        if (!isNaN(result))
+            throw "Error: bad result, 1 ** Number.POSITIVE_INFINITY = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = (-1) ** Number.POSITIVE_INFINITY;
+        if (!isNaN(result))
+            throw "Error: bad result, -1 ** Number.POSITIVE_INFINITY = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = 1 ** Number.NEGATIVE_INFINITY;
+        if (!isNaN(result))
+            throw "Error: bad result, 1 ** Number.NEGATIVE_INFINITY = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = (-1) ** Number.NEGATIVE_INFINITY;
+        if (!isNaN(result))
+            throw "Error: bad result, -1 ** Number.NEGATIVE_INFINITY = " + result;
+    }
+}
+noInline(infiniteExponentsStatic);
+infiniteExponentsStatic();
+
+function mathPowInfiniteExponentsDynamic1(x, y) {
+    return x ** y;
+}
+function mathPowInfiniteExponentsDynamic2(x, y) {
+    return x ** y;
+}
+function mathPowInfiniteExponentsDynamic3(x, y) {
+    return x ** y;
+}
+function mathPowInfiniteExponentsDynamic4(x, y) {
+    return x ** y;
+}
+noInline(mathPowInfiniteExponentsDynamic1);
+noInline(mathPowInfiniteExponentsDynamic2);
+noInline(mathPowInfiniteExponentsDynamic3);
+noInline(mathPowInfiniteExponentsDynamic4);
+function infiniteExponentsDynamic() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowInfiniteExponentsDynamic1(1, Number.POSITIVE_INFINITY);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowInfiniteExponentsDynamic1(1, Number.POSITIVE_INFINITY) = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowInfiniteExponentsDynamic2(-1, Number.POSITIVE_INFINITY);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowInfiniteExponentsDynamic2(-1, Number.POSITIVE_INFINITY) = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowInfiniteExponentsDynamic3(1, Number.NEGATIVE_INFINITY);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowInfiniteExponentsDynamic3(1, Number.NEGATIVE_INFINITY) = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = mathPowInfiniteExponentsDynamic4(-1, Number.NEGATIVE_INFINITY);
+        if (!isNaN(result))
+            throw "Error: bad result, mathPowInfiniteExponentsDynamic4(-1, Number.NEGATIVE_INFINITY) = " + result;
+    }
+}
+noInline(infiniteExponentsDynamic);
+infiniteExponentsDynamic();
diff --git a/Source/JavaScriptCore/tests/stress/pow-simple.js b/Source/JavaScriptCore/tests/stress/pow-simple.js
new file mode 100644 (file)
index 0000000..be5e9cf
--- /dev/null
@@ -0,0 +1,39 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let i = 2;
+    let j = 3;
+    shouldBe(2 ** 3, 8);
+    shouldBe(i ** 3, 8);
+    shouldBe(2 ** j, 8);
+    shouldBe(i ** j, 8);
+}
+
+{
+    shouldBe(2 ** 3 ** 3, 134217728);
+    shouldBe(2 ** 3 + 3, 11);
+    shouldBe(2 ** 3 + 3 ** 3, 35);
+    shouldBe(2 ** 3 * 3, 24);
+    shouldBe(2 ** 3 * 3 ** 3, 216);
+
+    shouldBe(2 + 3 ** 3, 29);
+    shouldBe(2 * 3 ** 3, 54);
+}
+
+{
+    let i = 2;
+    i **= 4;
+    shouldBe(i, 16);
+    i **= 1 + 1;
+    shouldBe(i, 256);
+}
+
+for (let i = 0; i < 1e4; ++i) {
+    let a = Math.random();
+    let b = Math.random();
+    shouldBe(a ** b, Math.pow(a, b));
+}
diff --git a/Source/JavaScriptCore/tests/stress/pow-stable-results.js b/Source/JavaScriptCore/tests/stress/pow-stable-results.js
new file mode 100644 (file)
index 0000000..9828fa9
--- /dev/null
@@ -0,0 +1,85 @@
+// This test verify the results of `**` do not change as we change optimization tier.
+
+function opaquePow(a, b)
+{
+    return a ** b;
+}
+noInline(opaquePow);
+
+
+let caseStrings = [
+    "0",
+    "-0.",
+    "0.5",
+    "1",
+    "2",
+    "-0.5",
+    "-1",
+    "999",
+    "1000",
+    "1001",
+    "NaN",
+    "Infinity",
+    "-Infinity",
+    "Math.PI",
+    "Math.LN2",
+    "Math.LN10",
+    "Math.E",
+    "Math.LOG10E",
+    "Math.LOG2E",
+    "Math.SQRT2"
+];
+let cases = [];
+for (let caseString of caseStrings) {
+    cases.push(eval(caseString));
+}
+
+let expectedResults = [];
+let constantBaseFunctions = [];
+let constantExponentFunctions = [];
+for (let i = 0; i < cases.length; ++i) {
+    let base = cases[i];
+
+    expectedResults[i] = [];
+    for (let j = 0; j < cases.length; ++j) {
+        let exponent = cases[j];
+        expectedResults[i][j] = base ** exponent;
+    }
+
+    eval("constantBaseFunctions[i] = function (exponent) { return (" + caseStrings[i] + ") ** exponent; }");
+    eval("constantExponentFunctions[i] = function (base) { return base ** (" + caseStrings[i] + "); }");
+}
+
+function isIdentical(result, expected)
+{
+    if (expected === expected) {
+        if (result !== expected)
+            return false;
+        if (!expected && 1 / expected === -Infinity && 1 / result !== -Infinity)
+            return false;
+
+        return true;
+    }
+    return result !== result;
+}
+
+for (let tierUpLoopCounter = 0; tierUpLoopCounter < 1e3; ++tierUpLoopCounter) {
+    for (let i = 0; i < cases.length; ++i) {
+        let base = cases[i];
+        for (let j = 0; j < cases.length; ++j) {
+            let exponent = cases[j];
+            let expectedResult = expectedResults[i][j];
+            let result = opaquePow(base, exponent);
+            if (!isIdentical(result, expectedResult))
+                throw `Failed opaquePow with base = ${base} exponent = ${exponent} expected (${expectedResult}) got (${result})`;
+
+            result = constantBaseFunctions[i](exponent);
+            if (!isIdentical(result, expectedResult))
+                throw `Failed constantBaseFunctions with base = ${base} exponent = ${exponent} expected (${expectedResult}) got (${result})`;
+
+            result = constantExponentFunctions[j](base);
+            if (!isIdentical(result, expectedResult))
+                throw `Failed constantExponentFunctions with base = ${base} exponent = ${exponent} expected (${expectedResult}) got (${result})`;
+        }
+    }
+}
diff --git a/Source/JavaScriptCore/tests/stress/pow-to-number-should-be-executed-in-code-side.js b/Source/JavaScriptCore/tests/stress/pow-to-number-should-be-executed-in-code-side.js
new file mode 100644 (file)
index 0000000..873107a
--- /dev/null
@@ -0,0 +1,77 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let value = {
+        valueOf()
+        {
+            throw new Error("NG");
+        }
+    };
+    let error = null;
+
+    try {
+        2 ** value;
+    } catch (e) {
+        error = e;
+    }
+    // global, and valueOf.
+    shouldBe(error.stack.split("\n").length, 2);
+}
+
+{
+    let value = {
+        valueOf()
+        {
+            throw new Error("NG");
+        }
+    };
+    let error = null;
+
+    try {
+        value ** 2;
+    } catch (e) {
+        error = e;
+    }
+    // global, and valueOf.
+    shouldBe(error.stack.split("\n").length, 2);
+}
+
+{
+    let value = {
+        valueOf()
+        {
+            throw new Error("NG");
+        }
+    };
+    let error = null;
+
+    try {
+        Math.pow(value, 2);
+    } catch (e) {
+        error = e;
+    }
+    // global, Math.pow, and valueOf.
+    shouldBe(error.stack.split("\n").length, 3);
+}
+
+{
+    let value = {
+        valueOf()
+        {
+            throw new Error("NG");
+        }
+    };
+    let error = null;
+
+    try {
+        Math.pow(2, value);
+    } catch (e) {
+        error = e;
+    }
+    // global, Math.pow, and valueOf.
+    shouldBe(error.stack.split("\n").length, 3);
+}
diff --git a/Source/JavaScriptCore/tests/stress/pow-with-constants.js b/Source/JavaScriptCore/tests/stress/pow-with-constants.js
new file mode 100644 (file)
index 0000000..79e6af7
--- /dev/null
@@ -0,0 +1,285 @@
+function exponentIsZero(x) {
+    return x ** 0;
+}
+noInline(exponentIsZero);
+
+function testExponentIsZero() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsZero(5);
+        if (result !== 1)
+            throw "Error: zeroExponent(5) should be 1, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsZero(5.5);
+        if (result !== 1)
+            throw "Error: zeroExponent(5.5) should be 1, was = " + result;
+    }
+}
+testExponentIsZero();
+
+
+function exponentIsOne(x) {
+    return x ** 1;
+}
+noInline(exponentIsOne);
+
+function testExponentIsOne() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsOne(5);
+        if (result !== 5)
+            throw "Error: exponentIsOne(5) should be 5, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsOne(5.5);
+        if (result !== 5.5)
+            throw "Error: exponentIsOne(5.5) should be 5.5, was = " + result;
+    }
+}
+testExponentIsOne();
+
+
+function powUsedAsSqrt(x) {
+    return x ** 0.5;
+}
+noInline(powUsedAsSqrt);
+
+function testPowUsedAsSqrt() {
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsSqrt(4);
+        if (result !== Math.sqrt(4))
+            throw "Error: powUsedAsSqrt(4) should be 2, was = " + result;
+        result = powUsedAsSqrt(4.4);
+        if (result !== Math.sqrt(4.4))
+            throw "Error: powUsedAsSqrt(4) should be " + Math.sqrt(4.4) + ", was = " + result;
+        if (powUsedAsSqrt(Infinity) !== Infinity)
+            throw "Failed powUsedAsSqrt(Infinity)";
+        if (powUsedAsSqrt(-Infinity) !== Infinity)
+            throw "Failed powUsedAsSqrt(-Infinity)";
+        let nanResult = powUsedAsSqrt(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsSqrt(NaN)";
+        let zeroResult = powUsedAsSqrt(0.)
+        if (zeroResult || (1 / zeroResult) !== Infinity)
+            throw "Failed powUsedAsSqrt(0.)";
+        let negativeZeroResult = powUsedAsSqrt(-0)
+        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
+            throw "Failed powUsedAsSqrt(-0)";
+    }
+}
+testPowUsedAsSqrt();
+
+function powUsedAsOneOverSqrt(x) {
+    return x ** -0.5;
+}
+noInline(powUsedAsOneOverSqrt);
+
+function testPowUsedAsOneOverSqrt() {
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsOneOverSqrt(4);
+        if (result !== 0.5)
+            throw "Error: powUsedAsOneOverSqrt(4) should be 0.5, was = " + result;
+        result = powUsedAsOneOverSqrt(4.4);
+        if (result !== 1/Math.sqrt(4.4))
+            throw "Error: powUsedAsOneOverSqrt(4) should be " + 1/Math.sqrt(4.4) + ", was = " + result;
+        if (powUsedAsOneOverSqrt(Infinity) !== 0)
+            throw "Failed powUsedAsOneOverSqrt(Infinity)";
+        if (powUsedAsOneOverSqrt(-Infinity) !== 0)
+            throw "Failed powUsedAsOneOverSqrt(-Infinity)";
+        let nanResult = powUsedAsOneOverSqrt(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsOneOverSqrt(NaN)";
+        if (powUsedAsOneOverSqrt(0) !== Infinity)
+            throw "Failed powUsedAsOneOverSqrt(0)";
+        if (powUsedAsOneOverSqrt(-0.) !== Infinity)
+            throw "Failed powUsedAsOneOverSqrt(-0.)";
+    }
+}
+testPowUsedAsOneOverSqrt();
+
+function powUsedAsSquare(x) {
+    return x ** 2;
+}
+noInline(powUsedAsSquare);
+
+function testPowUsedAsSquare() {
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsSquare(2);
+        if (result !== 4)
+            throw "Error: powUsedAsSquare(4) should be 2, was = " + result;
+        result = powUsedAsSquare(4.4);
+        if (result !== 19.360000000000003)
+            throw "Error: powUsedAsSquare(4) should be " + 19.360000000000003 + ", was = " + result;
+        result = powUsedAsSquare(Math.PI);
+        if (result !== 9.869604401089358)
+            throw "Error: powUsedAsSquare(4) should be " + 9.869604401089358 + ", was = " + result;
+        if (powUsedAsSquare(Infinity) !== Infinity)
+            throw "Failed powUsedAsSquare(Infinity)";
+        if (powUsedAsSquare(-Infinity) !== Infinity)
+            throw "Failed powUsedAsSquare(-Infinity)";
+        let nanResult = powUsedAsSquare(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsSquare(NaN)";
+        let zeroResult = powUsedAsSquare(0.)
+        if (zeroResult || (1 / zeroResult) !== Infinity)
+            throw "Failed powUsedAsSquare(0.)";
+        let negativeZeroResult = powUsedAsSquare(-0)
+        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
+            throw "Failed powUsedAsSquare(-0)";
+    }
+}
+testPowUsedAsSquare();
+
+function intIntConstantsSmallNumbers() {
+    return 42 ** 3;
+}
+function intIntConstantsLargeNumbers() {
+    // The result does not fit in a integer.
+    return 42 ** 42;
+}
+function intIntSmallConstants() {
+    return 42 ** 3;
+}
+function intDoubleConstants() {
+    return 14 ** 42.5;
+}
+function doubleDoubleConstants() {
+    return 13.5 ** 42.5;
+}
+function doubleIntConstants() {
+    return 13.5 ** 52;
+}
+noInline(intIntConstantsSmallNumbers);
+noInline(intIntConstantsLargeNumbers);
+noInline(intDoubleConstants);
+noInline(doubleDoubleConstants);
+noInline(doubleIntConstants);
+
+function testBaseAndExponentConstantLiterals()
+{
+    for (var i = 0; i < 10000; ++i) {
+        var result = intIntConstantsSmallNumbers();
+        if (result !== 74088)
+            throw "Error: intIntConstantsSmallNumbers() should be 74088, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = intIntConstantsLargeNumbers();
+        if (result !== 1.5013093754529656e+68)
+            throw "Error: intIntConstantsLargeNumbers() should be 1.5013093754529656e+68, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = intDoubleConstants();
+        if (result !== 5.1338303882015765e+48)
+            throw "Error: intDoubleConstants() should be 5.1338303882015765e+48, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = doubleDoubleConstants();
+        if (result !== 1.0944228729647829e+48)
+            throw "Error: doubleDoubleConstants() should be 1.0944228729647829e+48, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = doubleIntConstants();
+        if (result !== 5.989022735311158e+58)
+            throw "Error: doubleIntConstants() should be 5.989022735311158e+58, was = " + result;
+    }
+}
+testBaseAndExponentConstantLiterals();
+
+
+function exponentIsIntegerConstant(x) {
+    return x ** 42;
+}
+noInline(exponentIsIntegerConstant);
+
+function testExponentIsIntegerConstant() {
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsIntegerConstant(2);
+        if (result !== 4398046511104)
+            throw "Error: exponentIsIntegerConstant(2) should be 4398046511104, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsIntegerConstant(5);
+        if (result !== 2.2737367544323207e+29)
+            throw "Error: exponentIsIntegerConstant(5) should be 2.2737367544323207e+29, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsIntegerConstant(2.1);
+        if (result !== 34135823067412.42)
+            throw "Error: exponentIsIntegerConstant(2.1) should be 34135823067412.42, was = " + result;
+    }
+}
+testExponentIsIntegerConstant();
+
+
+function exponentIsDoubleConstant(x) {
+    return x ** 42.5;
+}
+noInline(exponentIsDoubleConstant);
+
+function testExponentIsDoubleConstant() {
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsDoubleConstant(2);
+        if (result !== 6219777023950.95)
+            throw "Error: exponentIsDoubleConstant(2) should be 6219777023950.95, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsDoubleConstant(5);
+        if (result !== 5.084229945850415e+29)
+            throw "Error: exponentIsDoubleConstant(5) should be 5.084229945850415e+29, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsDoubleConstant(2.1);
+        if (result !== 49467507261113.805)
+            throw "Error: exponentIsDoubleConstant(2.1) should be 49467507261113.805, was = " + result;
+    }
+}
+testExponentIsDoubleConstant();
+
+
+function exponentIsInfinityConstant(x) {
+    return x ** Infinity;
+}
+noInline(exponentIsInfinityConstant);
+
+function testExponentIsInfinityConstant() {
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsInfinityConstant(2);
+        if (result !== Infinity)
+            throw "Error: exponentIsInfinityConstant(2) should be Infinity, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsInfinityConstant(5);
+        if (result !== Infinity)
+            throw "Error: exponentIsInfinityConstant(5) should be Infinity, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsInfinityConstant(2.1);
+        if (result !== Infinity)
+            throw "Error: exponentIsInfinityConstant(2.1) should be Infinity, was = " + result;
+    }
+}
+testExponentIsInfinityConstant();
+
+
+function exponentIsNegativeInfinityConstant(x) {
+    return x ** -Infinity;
+}
+noInline(exponentIsNegativeInfinityConstant);
+
+function testExponentIsNegativeInfinityConstant() {
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsNegativeInfinityConstant(2);
+        if (result !== 0)
+            throw "Error: exponentIsNegativeInfinityConstant(2) should be zero, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsNegativeInfinityConstant(5);
+        if (result !== 0)
+            throw "Error: exponentIsNegativeInfinityConstant(5) should be zero, was = " + result;
+    }
+    for (var i = 0; i < 1000; ++i) {
+        var result = exponentIsNegativeInfinityConstant(2.1);
+        if (result !== 0)
+            throw "Error: exponentIsNegativeInfinityConstant(2.1) should be zero, was = " + result;
+    }
+}
+testExponentIsNegativeInfinityConstant();
diff --git a/Source/JavaScriptCore/tests/stress/pow-with-never-NaN-exponent.js b/Source/JavaScriptCore/tests/stress/pow-with-never-NaN-exponent.js
new file mode 100644 (file)
index 0000000..cd6b991
--- /dev/null
@@ -0,0 +1,24 @@
+function exponentIsNonNanDouble1(x, doubleArrayIndex) {
+    var doubleArray = [4.4];
+    return x ** doubleArray[doubleArrayIndex];
+}
+noInline(exponentIsNonNanDouble1);
+
+function exponentIsNonNanDouble2(x, doubleArray) {
+    return x ** doubleArray[0];
+}
+noInline(exponentIsNonNanDouble2);
+
+function testExponentIsDoubleConstant() {
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsNonNanDouble1(2, 0);
+        if (result !== 21.112126572366314)
+            throw "Error: exponentIsNonNanDouble1(2, 0) should be 21.112126572366314, was = " + result;
+    }
+    for (var i = 0; i < 10000; ++i) {
+        var result = exponentIsNonNanDouble2(3, [-1.5]);
+        if (result !== 0.19245008972987526)
+            throw "Error: exponentIsNonNanDouble2(3, [-1.5]) should be 0.19245008972987526, was = " + result;
+    }
+}
+testExponentIsDoubleConstant();
index 0c845b3..845d54d 100644 (file)
 - path: test262/test/language/expressions/equals/symbol-strict-equality-comparison.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A1.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A1.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A10.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A10.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A11.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A11.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A12.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A12.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A13.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A13.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A14.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A14.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A15.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A15.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A16.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A16.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A17.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A17.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A18.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A18.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A19.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A19.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A2.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A2.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A20.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A20.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A21.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A21.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A22.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A22.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A23.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A23.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A3.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A3.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A4.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A4.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A5.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A5.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A6.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A6.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A7.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A7.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A8.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A8.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A9.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/applying-the-exp-operator_A9.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-assignment-operator.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-assignment-operator.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-operator-evaluation-order.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-operator-evaluation-order.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-operator-precedence-unary-expression-semantics.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-operator-precedence-unary-expression-semantics.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-operator-precedence-update-expression-semantics.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-operator-precedence-update-expression-semantics.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-operator-syntax-error-bitnot-unary-expression-base.js
   cmd: runTest262 :normal, "SyntaxError", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-operator-syntax-error-bitnot-unary-expression-base.js
 - path: test262/test/language/expressions/exponentiation/exp-operator-syntax-error-void-unary-expression-base.js
   cmd: runTest262 :normal, "SyntaxError", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/exponentiation/exp-operator.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/exponentiation/exp-operator.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/function/S10.1.1_A1_T2.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/function/S10.1.1_A1_T2.js