diff --git a/include/hermes/BCGen/HBC/BytecodeFileFormat.h b/include/hermes/BCGen/HBC/BytecodeFileFormat.h index a81a01d928e..ffeb462581e 100644 --- a/include/hermes/BCGen/HBC/BytecodeFileFormat.h +++ b/include/hermes/BCGen/HBC/BytecodeFileFormat.h @@ -28,8 +28,8 @@ const static uint64_t MAGIC = 0x1F1903C103BC1FC6; const static uint64_t DELTA_MAGIC = ~MAGIC; // Bytecode version generated by this version of the compiler. -// Updated: Aug 1, 2019 -const static uint32_t BYTECODE_VERSION = 61; +// Updated: Aug 28, 2019 +const static uint32_t BYTECODE_VERSION = 62; /// Property cache index which indicates no caching. static constexpr uint8_t PROPERTY_CACHING_DISABLED = 0; diff --git a/include/hermes/BCGen/Lowering.h b/include/hermes/BCGen/Lowering.h index 80c059b874a..4cd7b9bd9ea 100644 --- a/include/hermes/BCGen/Lowering.h +++ b/include/hermes/BCGen/Lowering.h @@ -121,6 +121,25 @@ class LowerCondBranch : public FunctionPass { static bool isOperatorSupported(BinaryOperatorInst::OpKind op); }; +/// Iterates over all instructions and performs lowering on exponentiation +/// operators to turn them into HermesInternal calls. +/// NOTE: It may be possible in the future to extend this pass to allow for +/// other lowering operations on single instructions. +class LowerExponentiationOperator : public FunctionPass { + public: + explicit LowerExponentiationOperator() + : FunctionPass("LowerExponentiationOperator") {} + ~LowerExponentiationOperator() override = default; + bool runOnFunction(Function *F) override; + + private: + /// Changes the binary exponentiation operator \p inst into a call to + /// HermesInternal.exponentiationOperator. + static bool lowerExponentiationOperator( + IRBuilder &builder, + BinaryOperatorInst *inst); +}; + } // namespace hermes #endif diff --git a/include/hermes/IR/Instrs.h b/include/hermes/IR/Instrs.h index 8464c605de8..b0641cc9ad7 100644 --- a/include/hermes/IR/Instrs.h +++ b/include/hermes/IR/Instrs.h @@ -1599,6 +1599,7 @@ class BinaryOperatorInst : public Instruction { OrKind, // | (|=) XorKind, // ^ (^=) AndKind, // & (^=) + ExponentiationKind, // ** (**=) InKind, // "in" InstanceOfKind, // instanceof LAST_OPCODE diff --git a/include/hermes/Inst/Builtins.def b/include/hermes/Inst/Builtins.def index c7f1fe50ded..becb0ef323c 100644 --- a/include/hermes/Inst/Builtins.def +++ b/include/hermes/Inst/Builtins.def @@ -43,6 +43,7 @@ BUILTIN_METHOD(HermesInternal, ensureObject) BUILTIN_METHOD(HermesInternal, copyDataProperties) BUILTIN_METHOD(HermesInternal, copyRestArgs) BUILTIN_METHOD(HermesInternal, exportAll) +BUILTIN_METHOD(HermesInternal, exponentiationOperator) BUILTIN_OBJECT(JSON) BUILTIN_METHOD(JSON, parse) diff --git a/include/hermes/Optimizer/PassManager/Pass.h b/include/hermes/Optimizer/PassManager/Pass.h index f0bb788ca11..dc4fa0a19bc 100644 --- a/include/hermes/Optimizer/PassManager/Pass.h +++ b/include/hermes/Optimizer/PassManager/Pass.h @@ -15,6 +15,8 @@ using llvm::StringRef; class Function; class Module; +class Instruction; +class IRBuilder; /// This class represents a pass, which is a transformation of the IR. Passes /// are either Function passes, which transform one function, and are not diff --git a/include/hermes/Parser/TokenKinds.def b/include/hermes/Parser/TokenKinds.def index 6f626eac8c0..eab8f289ff8 100644 --- a/include/hermes/Parser/TokenKinds.def +++ b/include/hermes/Parser/TokenKinds.def @@ -96,6 +96,7 @@ PUNCTUATOR(comma, ",") PUNCTUATOR(plusplus, "++") PUNCTUATOR(minusminus, "--") RANGE_MARKER(_first_binary) +BINOP( starstar, "**", 11) BINOP( star, "*", 10) BINOP( percent, "%", 10) BINOP( slash, "/", 10) @@ -126,6 +127,7 @@ PUNCTUATOR(equal, "=") PUNCTUATOR(plusequal, "+=") PUNCTUATOR(minusequal, "-=") PUNCTUATOR(starequal, "*=") +PUNCTUATOR(starstarequal, "**=") PUNCTUATOR(percentequal, "%=") PUNCTUATOR(slashequal, "/=") PUNCTUATOR(lesslessequal, "<<=") diff --git a/include/hermes/Support/Math.h b/include/hermes/Support/Math.h new file mode 100644 index 00000000000..36e34e5478d --- /dev/null +++ b/include/hermes/Support/Math.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#ifndef HERMES_SUPPORT_MATH_H +#define HERMES_SUPPORT_MATH_H + +#include + +namespace hermes { + +/// ES9.0 12.6.4 +/// Perform the exponentiation operation on doubles required by the JS spec. +inline double expOp(double x, double y) { + constexpr double nan = std::numeric_limits::quiet_NaN(); + + // Handle special cases that std::pow handles differently. + if (std::isnan(y)) { + return nan; + } else if (y == 0) { + return 1; + } else if (std::abs(x) == 1 && std::isinf(y)) { + return nan; + } + + // std::pow handles the other edge cases as the ES9.0 spec requires. + return std::pow(x, y); +} + +} // namespace hermes + +#endif diff --git a/include/hermes/VM/PredefinedStrings.def b/include/hermes/VM/PredefinedStrings.def index 9f942c3d81d..bb2a7e8ac1b 100644 --- a/include/hermes/VM/PredefinedStrings.def +++ b/include/hermes/VM/PredefinedStrings.def @@ -391,6 +391,7 @@ STR(ensureObject, "ensureObject") STR(copyDataProperties, "copyDataProperties") STR(copyRestArgs, "copyRestArgs") STR(exportAll, "exportAll") +STR(exponentiationOperator, "exponentiationOperator") STR(require, "require") STR(requireFast, "requireFast") diff --git a/lib/BCGen/HBC/HBC.cpp b/lib/BCGen/HBC/HBC.cpp index 89483c15485..7cf4975ee59 100644 --- a/lib/BCGen/HBC/HBC.cpp +++ b/lib/BCGen/HBC/HBC.cpp @@ -53,7 +53,10 @@ void lowerIR(Module *M, const BytecodeGenerationOptions &options) { // OptEnvironmentInit needs to run before LowerConstants. PM.addPass(new OptEnvironmentInit()); } - /// LowerBuiltinCalls needs to run before the rest of the lowering. + // LowerExponentiationOperator needs to run before LowerBuiltinCalls because + // it introduces calls to HermesInternal. + PM.addPass(new LowerExponentiationOperator()); + // LowerBuiltinCalls needs to run before the rest of the lowering. PM.addPass(new LowerBuiltinCalls()); // It is important to run LowerNumericProperties before LoadConstants // as LowerNumericProperties could generate new constants. diff --git a/lib/BCGen/HBC/ISel.cpp b/lib/BCGen/HBC/ISel.cpp index 0d9298598b9..5f88a08b82d 100644 --- a/lib/BCGen/HBC/ISel.cpp +++ b/lib/BCGen/HBC/ISel.cpp @@ -518,6 +518,9 @@ void HBCISel::generateBinaryOperatorInst( BCFGen_->emitDiv(res, left, right); } break; + case OpKind::ExponentiationKind: // ** (**=) + llvm_unreachable("ExponentiationKind emits a HermesInternal call"); + break; case OpKind::ModuloKind: // % (%=) BCFGen_->emitMod(res, left, right); break; diff --git a/lib/BCGen/Lowering.cpp b/lib/BCGen/Lowering.cpp index f3a6d34c1d7..11d82b3a767 100644 --- a/lib/BCGen/Lowering.cpp +++ b/lib/BCGen/Lowering.cpp @@ -678,3 +678,46 @@ bool LowerCondBranch::runOnFunction(Function *F) { } return changed; } + +bool LowerExponentiationOperator::runOnFunction(Function *F) { + IRBuilder builder{F}; + llvm::DenseSet toTransform{}; + bool changed = false; + + for (BasicBlock &bb : *F) { + for (auto it = bb.begin(), e = bb.end(); it != e; /* empty */) { + auto *inst = &*it; + // Increment iterator before potentially erasing inst and invalidating + // iteration. + ++it; + if (auto *binOp = dyn_cast(inst)) { + if (binOp->getOperatorKind() == + BinaryOperatorInst::OpKind::ExponentiationKind) { + changed |= lowerExponentiationOperator(builder, binOp); + } + } + } + } + + return changed; +} + +bool LowerExponentiationOperator::lowerExponentiationOperator( + IRBuilder &builder, + BinaryOperatorInst *binOp) { + assert( + binOp->getOperatorKind() == + BinaryOperatorInst::OpKind::ExponentiationKind && + "lowerExponentiationOperator must take a ** operator"); + // Replace a ** b with HermesInternal.exponentiationOperator(a, b) + builder.setInsertionPoint(binOp); + auto *result = builder.createCallInst( + builder.createLoadPropertyInst( + builder.createTryLoadGlobalPropertyInst("HermesInternal"), + "exponentiationOperator"), + builder.getLiteralUndefined(), + {binOp->getLeftHandSide(), binOp->getRightHandSide()}); + binOp->replaceAllUsesWith(result); + binOp->eraseFromParent(); + return true; +} diff --git a/lib/IR/IREval.cpp b/lib/IR/IREval.cpp index 4d54601b066..7d5db2dab46 100644 --- a/lib/IR/IREval.cpp +++ b/lib/IR/IREval.cpp @@ -6,6 +6,7 @@ */ #include "hermes/IR/IREval.h" #include "hermes/IR/IRBuilder.h" +#include "hermes/Support/Math.h" #include "llvm/ADT/SmallString.h" @@ -548,6 +549,12 @@ Literal *hermes::evalBinaryOperator( std::fmod(leftLiteralNum->getValue(), rightLiteralNum->getValue())); } + break; + case OpKind::ExponentiationKind: // ** (**=) + if (leftLiteralNum && rightLiteralNum) { + return builder.getLiteralNumber(hermes::expOp( + leftLiteralNum->getValue(), rightLiteralNum->getValue())); + } break; case OpKind::OrKind: // | (|=) if (leftLiteralNum && rightLiteralNum) { diff --git a/lib/IR/Instrs.cpp b/lib/IR/Instrs.cpp index cf2dfd7d26b..5d35a6ba521 100644 --- a/lib/IR/Instrs.cpp +++ b/lib/IR/Instrs.cpp @@ -55,12 +55,13 @@ const char *UnaryOperatorInst::opStringRepr[] = {"delete", "void", "typeof", "+", "-", "~", "!"}; const char *BinaryOperatorInst::opStringRepr[] = { - "", "==", "!=", "===", "!==", "<", "<=", ">", ">=", "<<", ">>", - ">>>", "+", "-", "*", "/", "%", "|", "^", "&", "in", "instanceof"}; + "", "==", "!=", "===", "!==", "<", "<=", ">", + ">=", "<<", ">>", ">>>", "+", "-", "*", "/", + "%", "|", "^", "&", "**", "in", "instanceof"}; const char *BinaryOperatorInst::assignmentOpStringRepr[] = { - "=", "", "", "", "", "", "", "", "", "<<=", ">>=", - ">>>=", "+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=", "", ""}; + "=", "", "", "", "", "", "", "", "", "<<=", ">>=", ">>>=", + "+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=", "**=", "", ""}; UnaryOperatorInst::OpKind UnaryOperatorInst::parseOperator(StringRef op) { for (int i = 0; i < static_cast(BinaryOperatorInst::OpKind::LAST_OPCODE); diff --git a/lib/Parser/JSLexer.cpp b/lib/Parser/JSLexer.cpp index 037ee6c0cf6..8ebd13a58fb 100644 --- a/lib/Parser/JSLexer.cpp +++ b/lib/Parser/JSLexer.cpp @@ -207,11 +207,28 @@ const Token *JSLexer::advance(GrammarContext grammarContext) { PUNC_L2_3('&', TokenKind::amp, '&', TokenKind::ampamp, '=', TokenKind::ampequal); PUNC_L2_3('|', TokenKind::pipe, '|', TokenKind::pipepipe, '=', TokenKind::pipeequal); + // * *= ** **= + case '*': + token_.setStart(curCharPtr_); + if (curCharPtr_[1] == '=') { + token_.setPunctuator(TokenKind::starequal); + curCharPtr_ += 2; + } else if (curCharPtr_[1] != '*') { + token_.setPunctuator(TokenKind::star); + curCharPtr_ += 1; + } else if (curCharPtr_[2] == '=') { + token_.setPunctuator(TokenKind::starstarequal); + curCharPtr_ += 3; + } else { + token_.setPunctuator(TokenKind::starstar); + curCharPtr_ += 2; + } + break; + // * *= // % %= // ^ ^= // / /= - PUNC_L2_2('*', TokenKind::star, '=', TokenKind::starequal); PUNC_L2_2('%', TokenKind::percent, '=', TokenKind::percentequal); PUNC_L2_2('^', TokenKind::caret, '=', TokenKind::caretequal); diff --git a/lib/Parser/JSParserImpl.cpp b/lib/Parser/JSParserImpl.cpp index bc1178e21d9..57ff9e27d8c 100644 --- a/lib/Parser/JSParserImpl.cpp +++ b/lib/Parser/JSParserImpl.cpp @@ -202,6 +202,7 @@ bool JSParserImpl::checkAssign() const { TokenKind::lesslessequal, TokenKind::greatergreaterequal, TokenKind::greatergreatergreaterequal, + TokenKind::starstarequal, TokenKind::ampequal, TokenKind::caretequal, TokenKind::pipeequal); @@ -2688,6 +2689,15 @@ Optional JSParserImpl::parseUnaryExpression() { if (!expr) return None; + if (check(TokenKind::starstar)) { + // ExponentiationExpression only allows UpdateExpressionNode on the + // left. The simplest way to enforce that the left operand is not + // an unparenthesized UnaryExpression is to check here. + sm_.error( + {startLoc, tok_->getEndLoc()}, + "Unary operator before ** must use parens to disambiguate"); + } + return setLocation( startLoc, expr.getValue(), @@ -2738,6 +2748,11 @@ inline unsigned getPrecedence(TokenKind kind) { return precedence[static_cast(kind)]; } +/// \return true if \p kind is left associative, false if right associative. +inline bool isLeftAssoc(TokenKind kind) { + return kind != TokenKind::starstar; +} + /// Return the precedence of \p kind unless it happens to be equal to \p except, /// in which case return 0. inline unsigned getPrecedenceExcept(TokenKind kind, TokenKind except) { @@ -2770,9 +2785,16 @@ Optional JSParserImpl::parseBinaryExpression(Param param) { // While the current token is a binary operator. while (unsigned precedence = getPrecedenceExcept(tok_->getKind(), exceptKind)) { - // If the next operator has lower precedence than the operator on the stack, - // pop the stack, creating a new binary expression. + // If the next operator has no greater precedence than the operator on the + // stack, pop the stack, creating a new binary expression. while (sp != STACK_SIZE && precedence <= getPrecedence(opStack[sp])) { + if (precedence == getPrecedence(opStack[sp]) && + !isLeftAssoc(opStack[sp])) { + // If the precedences are equal, then we avoid popping for + // right-associative operators to allow for the entire right-associative + // expression to be built from the right. + break; + } topExpr = newBinNode(valueStack[sp], opStack[sp], topExpr); ++sp; } diff --git a/lib/VM/JSLib/HermesInternal.cpp b/lib/VM/JSLib/HermesInternal.cpp index 4761a132dc6..bd03fada0ef 100644 --- a/lib/VM/JSLib/HermesInternal.cpp +++ b/lib/VM/JSLib/HermesInternal.cpp @@ -871,6 +871,7 @@ Handle createHermesInternalObject(Runtime *runtime) { defineInternMethod(P::ttiReached, hermesInternalTTIReached); defineInternMethod(P::ttrcReached, hermesInternalTTRCReached); defineInternMethod(P::exportAll, hermesInternalExportAll); + defineInternMethod(P::exponentiationOperator, mathPow); #ifdef HERMESVM_EXCEPTION_ON_OOM defineInternMethodAndSymbol("getCallStack", hermesInternalGetCallStack, 0); #endif // HERMESVM_EXCEPTION_ON_OOM diff --git a/lib/VM/JSLib/Math.cpp b/lib/VM/JSLib/Math.cpp index 79e29d25945..72b0182e8f2 100644 --- a/lib/VM/JSLib/Math.cpp +++ b/lib/VM/JSLib/Math.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "hermes/Support/Math.h" #include "hermes/Support/OSCompat.h" #include "llvm/Support/MathExtras.h" diff --git a/test/Optimizer/simplify.js b/test/Optimizer/simplify.js index de114f5bfaf..4aa8592814f 100644 --- a/test/Optimizer/simplify.js +++ b/test/Optimizer/simplify.js @@ -599,12 +599,14 @@ function equality(x, y) { //CHECK-NEXT: %0 = TryLoadGlobalPropertyInst globalObject : object, "print" : string //CHECK-NEXT: %1 = CallInst %0, undefined : undefined, 4 : number //CHECK-NEXT: %2 = CallInst %0, undefined : undefined, 8 : number -//CHECK-NEXT: %3 = ReturnInst undefined : undefined +//CHECK-NEXT: %3 = CallInst %0, undefined : undefined, 64 : number +//CHECK-NEXT: %4 = ReturnInst undefined : undefined //CHECK-NEXT:function_end function arith() { var sink = print; sink(2 * 2) sink(2 * 4) + sink(2 ** 6) } diff --git a/test/Parser/exp-error.js b/test/Parser/exp-error.js new file mode 100644 index 00000000000..40b04d43f0f --- /dev/null +++ b/test/Parser/exp-error.js @@ -0,0 +1,16 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the LICENSE +// file in the root directory of this source tree. +// +// RUN: (! %hermesc -dump-ast -pretty-json %s 2>&1 ) | %FileCheck --match-full-lines %s + ++3 ** 2; +// CHECK: {{.*}}:8:1: error: Unary operator before ** must use parens to disambiguate +// CHECK: +3 ** 2; +// CHECK: ^~~~~ + +delete 3 ** 2; +// CHECK: {{.*}}:13:1: error: Unary operator before ** must use parens to disambiguate +// CHECK: delete 3 ** 2; +// CHECK: ^~~~~~~~~~~ diff --git a/test/Parser/exp.js b/test/Parser/exp.js new file mode 100644 index 00000000000..fd60e6ab016 --- /dev/null +++ b/test/Parser/exp.js @@ -0,0 +1,145 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the LICENSE +// file in the root directory of this source tree. +// +// RUN: %hermes -dump-ast -pretty-json %s | %FileCheck %s --match-full-lines + +//CHECK: { +//CHECK-NEXT: "type": "Program", +//CHECK-NEXT: "body": [ + +2 ** 3; +// CHECK-NEXT: { +// CHECK-NEXT: "type": "ExpressionStatement", +// CHECK-NEXT: "expression": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "directive": null +// CHECK-NEXT: }, + +2 ** 3 ** 4; +// CHECK-NEXT: { +// CHECK-NEXT: "type": "ExpressionStatement", +// CHECK-NEXT: "expression": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "directive": null +// CHECK-NEXT: }, + +2 ** 3 ** 4 + 1; +// CHECK-NEXT: { +// CHECK-NEXT: "type": "ExpressionStatement", +// CHECK-NEXT: "expression": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "+" +// CHECK-NEXT: }, +// CHECK-NEXT: "directive": null +// CHECK-NEXT: }, + +1 + 2 ** 3 ** 4; +// CHECK-NEXT: { +// CHECK-NEXT: "type": "ExpressionStatement", +// CHECK-NEXT: "expression": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "BinaryExpression", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "NumericLiteral", +// CHECK-NEXT: "value": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "**" +// CHECK-NEXT: }, +// CHECK-NEXT: "operator": "+" +// CHECK-NEXT: }, +// CHECK-NEXT: "directive": null +// CHECK-NEXT: }, + +x **= y; +// CHECK-NEXT: { +// CHECK-NEXT: "type": "ExpressionStatement", +// CHECK-NEXT: "expression": { +// CHECK-NEXT: "type": "AssignmentExpression", +// CHECK-NEXT: "operator": "**=", +// CHECK-NEXT: "left": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x", +// CHECK-NEXT: "typeAnnotation": null +// CHECK-NEXT: }, +// CHECK-NEXT: "right": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "y", +// CHECK-NEXT: "typeAnnotation": null +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "directive": null +// CHECK-NEXT: } + +// CHECK-NEXT: ] +// CHECK-NEXT: } diff --git a/test/hermes/exp.js b/test/hermes/exp.js new file mode 100644 index 00000000000..50a5a5e0615 --- /dev/null +++ b/test/hermes/exp.js @@ -0,0 +1,38 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the LICENSE +// file in the root directory of this source tree. +// +// RUN: %hermes -O %s | %FileCheck --match-full-lines %s +// RUN: %hermes -O -emit-binary -out %t.hbc %s && %hermes %t.hbc | %FileCheck --match-full-lines %s + +'use strict' + +print('exponentiation'); +// CHECK-LABEL: exponentiation + +print(2 ** 3); +// CHECK-NEXT: 8 +print({valueOf: () => 2} ** 3); +// CHECK-NEXT: 8 + +print(2 ** 3 ** 2); +// CHECK-NEXT: 512 +print(2**3**2); +// CHECK-NEXT: 512 +print(2**(3**2)); +// CHECK-NEXT: 512 +print((2**3)**2); +// CHECK-NEXT: 64 + +print(1 + 2 ** 3 ** 2); +// CHECK-NEXT: 513 +print(2 ** 3 ** 2 + 1); +// CHECK-NEXT: 513 +print(Math.random() ** 0); +// CHECK-NEXT: 1 + +var x = 10; +x **= 2; +print(x); +// CHECK-NEXT: 100 diff --git a/tools/hbc-attribute/hbc-attribute.cpp b/tools/hbc-attribute/hbc-attribute.cpp index 8e1051bc6fa..fe20b7a894b 100644 --- a/tools/hbc-attribute/hbc-attribute.cpp +++ b/tools/hbc-attribute/hbc-attribute.cpp @@ -67,7 +67,7 @@ using SLG = hermes::hbc::SerializedLiteralGenerator; * If you have added or modified sections, make sure they're counted properly. */ static_assert( - BYTECODE_VERSION == 61, + BYTECODE_VERSION == 62, "Bytecode version changed. Please verify that hbc-attribute counts correctly.."); static llvm::cl::opt InputFilename( diff --git a/unittests/VMRuntime/PredefinedStrings.lock b/unittests/VMRuntime/PredefinedStrings.lock index 97f1b22429f..f6913a8496e 100644 --- a/unittests/VMRuntime/PredefinedStrings.lock +++ b/unittests/VMRuntime/PredefinedStrings.lock @@ -413,6 +413,7 @@ EXPECT("ensureObject") EXPECT("copyDataProperties") EXPECT("copyRestArgs") EXPECT("exportAll") +EXPECT("exponentiationOperator") EXPECT("require") EXPECT("requireFast") diff --git a/utils/testsuite/testsuite_blacklist.py b/utils/testsuite/testsuite_blacklist.py index ba3f5d757d1..5b249fc6160 100644 --- a/utils/testsuite/testsuite_blacklist.py +++ b/utils/testsuite/testsuite_blacklist.py @@ -206,7 +206,6 @@ "test262/test/language/expressions/await/", "test262/test/language/expressions/class/", "test262/test/language/expressions/assignment/destructuring/", - "test262/test/language/expressions/exponentiation/", "test262/test/language/expressions/new.target/", "test262/test/language/expressions/object/method-definition/", "test262/test/language/expressions/super/",