Commit 17c92fe6 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[es7] implement exponentiation operator proposal

Implements Stage 4 proposal from http://rwaldron.github.io/exponentiation-operator/,
without adding any knowledge of the feature to compiler backends.

BUG=v8:3915
LOG=Y
R=adamk@chromium.org, rossberg@chromium.org, littledan@chromium.org

Review URL: https://codereview.chromium.org/1678303002

Cr-Commit-Position: refs/heads/master@{#34890}
parent a4e5d154
......@@ -2276,6 +2276,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_instanceof)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_declarations)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_exponentiation_operator)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {
......@@ -2978,6 +2979,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_object_own_property_descriptors_natives[] = {
nullptr};
static const char* harmony_array_prototype_values_natives[] = {nullptr};
static const char* harmony_exponentiation_operator_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
......@@ -116,6 +116,7 @@ enum BindingFlags {
V(MAP_GET_METHOD_INDEX, JSFunction, map_get) \
V(MAP_HAS_METHOD_INDEX, JSFunction, map_has) \
V(MAP_SET_METHOD_INDEX, JSFunction, map_set) \
V(MATH_POW_METHOD_INDEX, JSFunction, math_pow) \
V(MESSAGE_GET_COLUMN_NUMBER_INDEX, JSFunction, message_get_column_number) \
V(MESSAGE_GET_LINE_NUMBER_INDEX, JSFunction, message_get_line_number) \
V(MESSAGE_GET_SOURCE_LINE_INDEX, JSFunction, message_get_source_line) \
......
......@@ -205,7 +205,8 @@ DEFINE_IMPLICATION(es_staging, harmony_tailcalls)
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_tailcalls, "harmony tail calls") \
V(harmony_regexp_property, "harmony unicode regexp property classes")
V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_exponentiation_operator, "harmony exponentiation operator `**`")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -202,6 +202,10 @@ function CubeRoot(x) {
// -------------------------------------------------------------------
%InstallToContext([
"math_pow", MathPowJS,
]);
%AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
// Set up math constants.
......
......@@ -121,6 +121,12 @@ class ParserBase : public Traits {
bool allow_##name() const { return allow_##name##_; } \
void set_allow_##name(bool allow) { allow_##name##_ = allow; }
#define SCANNER_ACCESSORS(name) \
bool allow_##name() const { return scanner_->allow_##name(); } \
void set_allow_##name(bool allow) { \
return scanner_->set_allow_##name(allow); \
}
ALLOW_ACCESSORS(lazy);
ALLOW_ACCESSORS(natives);
ALLOW_ACCESSORS(harmony_sloppy);
......@@ -131,6 +137,7 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_do_expressions);
ALLOW_ACCESSORS(harmony_function_name);
ALLOW_ACCESSORS(harmony_function_sent);
SCANNER_ACCESSORS(harmony_exponentiation_operator);
#undef ALLOW_ACCESSORS
uintptr_t stack_limit() const { return stack_limit_; }
......@@ -2009,6 +2016,11 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
Traits::SetFunctionNameFromIdentifierRef(right, expression);
}
if (op == Token::ASSIGN_EXP) {
DCHECK(!is_destructuring_assignment);
return Traits::RewriteAssignExponentiation(expression, right, pos);
}
ExpressionT result = factory()->NewAssignment(op, expression, right, pos);
if (is_destructuring_assignment) {
......@@ -2118,8 +2130,11 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
ArrowFormalParametersUnexpectedToken(classifier);
Token::Value op = Next();
int pos = position();
const bool is_right_associative = op == Token::EXP;
const int next_prec = is_right_associative ? prec1 : prec1 + 1;
ExpressionT y =
ParseBinaryExpression(prec1 + 1, accept_IN, classifier, CHECK_OK);
ParseBinaryExpression(next_prec, accept_IN, classifier, CHECK_OK);
Traits::RewriteNonPattern(classifier, CHECK_OK);
if (this->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos,
......@@ -2147,6 +2162,9 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
x = factory()->NewUnaryOperation(Token::NOT, x, pos);
}
}
} else if (op == Token::EXP) {
x = Traits::RewriteExponentiation(x, y, pos);
} else {
// We have a "normal" binary operation.
x = factory()->NewBinaryOperation(op, x, y, pos);
......@@ -2192,6 +2210,12 @@ ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier,
}
}
if (peek() == Token::EXP) {
ReportUnexpectedToken(Next());
*ok = false;
return this->EmptyExpression();
}
// Allow Traits do rewrite the expression.
return this->BuildUnaryExpression(expression, op, pos, factory());
} else if (Token::IsCountOp(op)) {
......
......@@ -439,6 +439,14 @@ bool ParserTraits::ShortcutNumericLiteralBinaryExpression(
*x = factory->NewNumberLiteral(value, pos, has_dot);
return true;
}
case Token::EXP: {
double value = std::pow(x_val, y_val);
int int_value = static_cast<int>(value);
*x = factory->NewNumberLiteral(
int_value == value && value != -0.0 ? int_value : value, pos,
has_dot);
return true;
}
default:
break;
}
......@@ -785,6 +793,8 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_function_sent(FLAG_harmony_function_sent);
set_allow_harmony_restrictive_declarations(
FLAG_harmony_restrictive_declarations);
set_allow_harmony_exponentiation_operator(
FLAG_harmony_exponentiation_operator);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -4681,6 +4691,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
SET_ALLOW(harmony_do_expressions);
SET_ALLOW(harmony_function_name);
SET_ALLOW(harmony_function_sent);
SET_ALLOW(harmony_exponentiation_operator);
#undef SET_ALLOW
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
......@@ -5371,6 +5382,16 @@ void ParserTraits::RewriteDestructuringAssignments() {
parser_->RewriteDestructuringAssignments();
}
Expression* ParserTraits::RewriteExponentiation(Expression* left,
Expression* right, int pos) {
return parser_->RewriteExponentiation(left, right, pos);
}
Expression* ParserTraits::RewriteAssignExponentiation(Expression* left,
Expression* right,
int pos) {
return parser_->RewriteAssignExponentiation(left, right, pos);
}
void ParserTraits::RewriteNonPattern(Type::ExpressionClassifier* classifier,
bool* ok) {
......@@ -5460,6 +5481,60 @@ void Parser::RewriteDestructuringAssignments() {
}
}
Expression* Parser::RewriteExponentiation(Expression* left, Expression* right,
int pos) {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
args->Add(left, zone());
args->Add(right, zone());
return factory()->NewCallRuntime(Context::MATH_POW_METHOD_INDEX, args, pos);
}
Expression* Parser::RewriteAssignExponentiation(Expression* left,
Expression* right, int pos) {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
if (left->IsVariableProxy()) {
VariableProxy* lhs = left->AsVariableProxy();
Expression* result;
DCHECK_NOT_NULL(lhs->raw_name());
result =
this->ExpressionFromIdentifier(lhs->raw_name(), lhs->position(),
lhs->end_position(), scope_, factory());
args->Add(left, zone());
args->Add(right, zone());
Expression* call =
factory()->NewCallRuntime(Context::MATH_POW_METHOD_INDEX, args, pos);
return factory()->NewAssignment(Token::ASSIGN, result, call, pos);
} else if (left->IsProperty()) {
Property* prop = left->AsProperty();
auto temp_obj = scope_->NewTemporary(ast_value_factory()->empty_string());
auto temp_key = scope_->NewTemporary(ast_value_factory()->empty_string());
Expression* assign_obj = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(temp_obj), prop->obj(),
RelocInfo::kNoPosition);
Expression* assign_key = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(temp_key), prop->key(),
RelocInfo::kNoPosition);
args->Add(factory()->NewProperty(factory()->NewVariableProxy(temp_obj),
factory()->NewVariableProxy(temp_key),
left->position()),
zone());
args->Add(right, zone());
Expression* call =
factory()->NewCallRuntime(Context::MATH_POW_METHOD_INDEX, args, pos);
Expression* target = factory()->NewProperty(
factory()->NewVariableProxy(temp_obj),
factory()->NewVariableProxy(temp_key), RelocInfo::kNoPosition);
Expression* assign =
factory()->NewAssignment(Token::ASSIGN, target, call, pos);
return factory()->NewBinaryOperation(
Token::COMMA, assign_obj,
factory()->NewBinaryOperation(Token::COMMA, assign_key, assign, pos),
pos);
}
UNREACHABLE();
return nullptr;
}
Expression* Parser::RewriteSpreads(ArrayLiteral* lit) {
// Array literals containing spreads are rewritten using do expressions, e.g.
......
......@@ -636,6 +636,11 @@ class ParserTraits {
// Rewrite all DestructuringAssignments in the current FunctionState.
V8_INLINE void RewriteDestructuringAssignments();
V8_INLINE Expression* RewriteExponentiation(Expression* left,
Expression* right, int pos);
V8_INLINE Expression* RewriteAssignExponentiation(Expression* left,
Expression* right, int pos);
V8_INLINE void QueueDestructuringAssignmentForRewriting(
Expression* assignment);
V8_INLINE void QueueNonPatternForRewriting(Expression* expr);
......@@ -1043,6 +1048,11 @@ class Parser : public ParserBase<ParserTraits> {
V8_INLINE void RewriteDestructuringAssignments();
V8_INLINE Expression* RewriteExponentiation(Expression* left,
Expression* right, int pos);
V8_INLINE Expression* RewriteAssignExponentiation(Expression* left,
Expression* right, int pos);
friend class NonPatternRewriter;
V8_INLINE Expression* RewriteSpreads(ArrayLiteral* lit);
......
......@@ -902,6 +902,16 @@ class PreParserTraits {
inline void RewriteDestructuringAssignments() {}
inline PreParserExpression RewriteExponentiation(PreParserExpression left,
PreParserExpression right,
int pos) {
return left;
}
inline PreParserExpression RewriteAssignExponentiation(
PreParserExpression left, PreParserExpression right, int pos) {
return left;
}
inline void QueueDestructuringAssignmentForRewriting(PreParserExpression) {}
inline void QueueNonPatternForRewriting(PreParserExpression) {}
......
......@@ -40,7 +40,8 @@ Scanner::Scanner(UnicodeCache* unicode_cache)
: unicode_cache_(unicode_cache),
bookmark_c0_(kNoBookmark),
octal_pos_(Location::invalid()),
found_html_comment_(false) {
found_html_comment_(false),
allow_harmony_exponentiation_operator_(false) {
bookmark_current_.literal_chars = &bookmark_current_literal_;
bookmark_current_.raw_literal_chars = &bookmark_current_raw_literal_;
bookmark_next_.literal_chars = &bookmark_next_literal_;
......@@ -565,7 +566,14 @@ void Scanner::Scan() {
case '*':
// * *=
token = Select('=', Token::ASSIGN_MUL, Token::MUL);
Advance();
if (c0_ == '*' && allow_harmony_exponentiation_operator()) {
token = Select('=', Token::ASSIGN_EXP, Token::EXP);
} else if (c0_ == '=') {
token = Select(Token::ASSIGN_MUL);
} else {
token = Token::MUL;
}
break;
case '%':
......
......@@ -450,6 +450,12 @@ class Scanner {
bool FoundHtmlComment() const { return found_html_comment_; }
#define DECLARE_ACCESSORS(name) \
inline bool allow_##name() const { return allow_##name##_; } \
inline void set_allow_##name(bool allow) { allow_##name##_ = allow; }
DECLARE_ACCESSORS(harmony_exponentiation_operator)
#undef ACCESSOR
private:
// The current and look-ahead token.
struct TokenDesc {
......@@ -758,6 +764,8 @@ class Scanner {
// Whether this scanner encountered an HTML comment.
bool found_html_comment_;
bool allow_harmony_exponentiation_operator_;
};
} // namespace internal
......
......@@ -63,6 +63,7 @@ namespace internal {
T(ASSIGN_MUL, "*=", 2) \
T(ASSIGN_DIV, "/=", 2) \
T(ASSIGN_MOD, "%=", 2) \
T(ASSIGN_EXP, "**=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
......@@ -82,6 +83,7 @@ namespace internal {
T(MUL, "*", 13) \
T(DIV, "/", 13) \
T(MOD, "%", 13) \
T(EXP, "**", 14) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
......@@ -214,12 +216,10 @@ class Token {
}
static bool IsAssignmentOp(Value tok) {
return INIT <= tok && tok <= ASSIGN_MOD;
return INIT <= tok && tok <= ASSIGN_EXP;
}
static bool IsBinaryOp(Value op) {
return COMMA <= op && op <= MOD;
}
static bool IsBinaryOp(Value op) { return COMMA <= op && op <= EXP; }
static bool IsTruncatingBinaryOp(Value op) {
return BIT_OR <= op && op <= ROR;
......
......@@ -1509,6 +1509,7 @@ enum ParserFlag {
kNoLegacyConst,
kAllowHarmonyFunctionSent,
kAllowHarmonyRestrictiveDeclarations,
kAllowHarmonyExponentiationOperator
};
enum ParserSyncTestResult {
......@@ -1529,6 +1530,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
flags.Contains(kAllowHarmonyFunctionSent));
parser->set_allow_harmony_restrictive_declarations(
flags.Contains(kAllowHarmonyRestrictiveDeclarations));
parser->set_allow_harmony_exponentiation_operator(
flags.Contains(kAllowHarmonyExponentiationOperator));
}
......@@ -7322,3 +7325,95 @@ TEST(FunctionDeclarationError) {
RunParserSyncTest(sloppy_context, sloppy_data, kSuccess, restrictive_flags,
arraysize(restrictive_flags));
}
TEST(ExponentiationOperator) {
// clang-format off
const char* context_data[][2] = {
{ "var O = { p: 1 }, x = 10; ; if (", ") { foo(); }" },
{ "var O = { p: 1 }, x = 10; ; (", ")" },
{ "var O = { p: 1 }, x = 10; foo(", ")" },
{ NULL, NULL }
};
const char* data[] = {
"(delete O.p) ** 10",
"(delete x) ** 10",
"(~O.p) ** 10",
"(~x) ** 10",
"(!O.p) ** 10",
"(!x) ** 10",
"(+O.p) ** 10",
"(+x) ** 10",
"(-O.p) ** 10",
"(-x) ** 10",
"(typeof O.p) ** 10",
"(typeof x) ** 10",
"(void 0) ** 10",
"(void O.p) ** 10",
"(void x) ** 10",
"++O.p ** 10",
"++x ** 10",
"--O.p ** 10",
"--x ** 10",
"O.p++ ** 10",
"x++ ** 10",
"O.p-- ** 10",
"x-- ** 10",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {
kAllowHarmonyExponentiationOperator};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ExponentiationOperatorErrors) {
// clang-format off
const char* context_data[][2] = {
{ "var O = { p: 1 }, x = 10; ; if (", ") { foo(); }" },
{ "var O = { p: 1 }, x = 10; ; (", ")" },
{ "var O = { p: 1 }, x = 10; foo(", ")" },
{ NULL, NULL }
};
const char* error_data[] = {
"delete O.p ** 10",
"delete x ** 10",
"~O.p ** 10",
"~x ** 10",
"!O.p ** 10",
"!x ** 10",
"+O.p ** 10",
"+x ** 10",
"-O.p ** 10",
"-x ** 10",
"typeof O.p ** 10",
"typeof x ** 10",
"void ** 10",
"void O.p ** 10",
"void x ** 10",
"++delete O.p ** 10",
"--delete O.p ** 10",
"++~O.p ** 10",
"++~x ** 10",
"--!O.p ** 10",
"--!x ** 10",
"++-O.p ** 10",
"++-x ** 10",
"--+O.p ** 10",
"--+x ** 10",
"[ x ] **= [ 2 ]",
"[ x **= 2 ] = [ 2 ]",
"{ x } **= { x: 2 }",
"{ x: x **= 2 ] = { x: 2 }",
// TODO(caitp): a Call expression as LHS should be an early ReferenceError!
// "Array() **= 10",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {
kAllowHarmonyExponentiationOperator};
RunParserSyncTest(context_data, error_data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-exponentiation-operator --harmony-proxies
function TestBasic() {
assertEquals(-(8 ** 2), -64);
assertEquals(+(8 ** 2), 64);
assertEquals(~(8 ** 2), -65);
assertEquals(!(8 ** 2), false);
assertEquals(2 ** -2, 0.25);
var o = { p: 1 };
assertEquals(2 ** delete o.p, 2);
assertEquals(2 ** void 1, NaN);
assertEquals(2 ** typeof 1, NaN);
var s = "2";
var n = 2;
assertEquals(2 ** "2", 4);
assertEquals(2 ** +"2", 4);
assertEquals(2 ** +s, 4);
assertEquals(2 ** s, 4);
assertEquals(2 ** 2, 4);
assertEquals(2 ** +2, 4);
assertEquals(2 ** +n, 4);
assertEquals(2 ** n, 4);
assertEquals(2 ** -"2", 0.25);
assertEquals(2 ** -s, 0.25);
assertEquals(2 ** -2, 0.25);
assertEquals(2 ** -n, 0.25);
assertEquals(2 ** ~"2", 0.125);
assertEquals(2 ** ~s, 0.125);
assertEquals(2 ** ~2, 0.125);
assertEquals(2 ** ~n, 0.125);
assertEquals(2 ** !"2", 1);
assertEquals(2 ** !s, 1);
assertEquals(2 ** !2, 1);
assertEquals(2 ** !n, 1);
var exponent = 2;
assertEquals(2 ** 3, 8);
assertEquals(3 * 2 ** 3, 24);
assertEquals(2 ** ++exponent, 8);
assertEquals(2 ** -1 * 2, 1);
assertEquals(2 ** 2 * 4, 16);
assertEquals(2 ** 2 / 2, 2);
assertEquals(2 ** (3 ** 2), 512);
assertEquals(2 ** 3 ** 2, 512);
assertEquals(2 * 3 ** 2, 18);
assertEquals(16 / 2 ** 2, 4);
}
TestBasic();
function TestAssignment() {
var base = -5;
assertEquals(base **= 3, -125);
assertEquals(base, -125);
}
TestAssignment();
function TestPrecedence() {
var base = 4;
assertEquals(--base ** 2, 9); // 3 ** 2
assertEquals(++base ** 2, 16); // 4 ** 2
assertEquals(base++ ** 2, 16); // 4 ** 2
assertEquals(base-- ** 2, 25); // 5 ** 2
assertEquals(4, base);
assertEquals(--base ** --base ** 2,
Math.pow(3, Math.pow(2, 2)));
assertEquals(2, base);
assertEquals(++base ** ++base ** 2,
Math.pow(3, Math.pow(4, 2)));
base = 4;
assertEquals(base-- ** base-- ** 2,
Math.pow(4, Math.pow(3, 2)));
assertEquals(2, base);
assertEquals(base++ ** base++ ** 2,
Math.pow(2, Math.pow(3, 2)));
}
TestPrecedence();
function TestInvariants() {
assertEquals(NaN, 2 ** NaN);
assertEquals(NaN, (+0) ** NaN);
assertEquals(NaN, (-0) ** NaN);
assertEquals(NaN, Infinity ** NaN);
assertEquals(NaN, (-Infinity) ** NaN);
assertEquals(1, NaN ** +0);
assertEquals(1, NaN ** -0);
assertEquals(NaN, NaN ** NaN);
assertEquals(NaN, NaN ** 2.2);
assertEquals(NaN, NaN ** 1);
assertEquals(NaN, NaN ** -1);
assertEquals(NaN, NaN ** -2.2);
assertEquals(NaN, NaN ** Infinity);
assertEquals(NaN, NaN ** -Infinity);
assertEquals(Infinity, 1.1 ** Infinity);
assertEquals(Infinity, (-1.1) ** Infinity);
assertEquals(Infinity, 2 ** Infinity);
assertEquals(Infinity, (-2) ** Infinity);
// Because +0 == -0, we need to compare 1/{+,-}0 to {+,-}Infinity
assertEquals(+Infinity, 1/1.1 ** -Infinity);
assertEquals(+Infinity, 1/(-1.1) ** -Infinity);
assertEquals(+Infinity, 1/2 ** -Infinity);
assertEquals(+Infinity, 1/(-2) ** -Infinity);
assertEquals(NaN, 1 ** Infinity);
assertEquals(NaN, 1 ** -Infinity);
assertEquals(NaN, (-1) ** Infinity);
assertEquals(NaN, (-1) ** -Infinity);
assertEquals(+0, 0.1 ** Infinity);
assertEquals(+0, (-0.1) ** Infinity);
assertEquals(+0, 0.999 ** Infinity);
assertEquals(+0, (-0.999) ** Infinity);
assertEquals(Infinity, 0.1 ** -Infinity);
assertEquals(Infinity, (-0.1) ** -Infinity);
assertEquals(Infinity, 0.999 ** -Infinity);
assertEquals(Infinity, (-0.999) ** -Infinity);
assertEquals(Infinity, Infinity ** 0.1);
assertEquals(Infinity, Infinity ** 2);
assertEquals(+Infinity, 1/Infinity ** -0.1);
assertEquals(+Infinity, 1/Infinity ** -2);
assertEquals(-Infinity, (-Infinity) ** 3);
assertEquals(-Infinity, (-Infinity) ** 13);
assertEquals(Infinity, (-Infinity) ** 3.1);
assertEquals(Infinity, (-Infinity) ** 2);
assertEquals(-Infinity, 1/(-Infinity) ** -3);
assertEquals(-Infinity, 1/(-Infinity) ** -13);
assertEquals(+Infinity, 1/(-Infinity) ** -3.1);
assertEquals(+Infinity, 1/(-Infinity) ** -2);
assertEquals(+Infinity, 1/(+0) ** 1.1);
assertEquals(+Infinity, 1/(+0) ** 2);
assertEquals(Infinity, (+0) ** -1.1);
assertEquals(Infinity, (+0) ** -2);
assertEquals(-Infinity, 1/(-0) ** 3);
assertEquals(-Infinity, 1/(-0) ** 13);
assertEquals(+Infinity, 1/(-0) ** 3.1);
assertEquals(+Infinity, 1/(-0) ** 2);
assertEquals(-Infinity, (-0) ** -3);
assertEquals(-Infinity, (-0) ** -13);
assertEquals(Infinity, (-0) ** -3.1);
assertEquals(Infinity, (-0) ** -2);
assertEquals(NaN, (-0.00001) ** 1.1);
assertEquals(NaN, (-0.00001) ** -1.1);
assertEquals(NaN, (-1.1) ** 1.1);
assertEquals(NaN, (-1.1) ** -1.1);
assertEquals(NaN, (-2) ** 1.1);
assertEquals(NaN, (-2) ** -1.1);
assertEquals(NaN, (-1000) ** 1.1);
assertEquals(NaN, (-1000) ** -1.1);
assertEquals(+Infinity, 1/(-0) ** 0.5);
assertEquals(+Infinity, 1/(-0) ** 0.6);
assertEquals(-Infinity, 1/(-0) ** 1);
assertEquals(-Infinity, 1/(-0) ** 10000000001);
assertEquals(+Infinity, (-0) ** -0.5);
assertEquals(+Infinity, (-0) ** -0.6);
assertEquals(-Infinity, (-0) ** -1);
assertEquals(-Infinity, (-0) ** -10000000001);
assertEquals(4, 16 ** 0.5);
assertEquals(NaN, (-16) ** 0.5);
assertEquals(0.25, 16 ** -0.5);
assertEquals(NaN, (-16) ** -0.5);
}
TestInvariants();
function TestOperationOrder() {
var log = [];
var handler = {
get(t, n) {
var result = Reflect.get(t, n);
var str = typeof result === "object" ? "[object Object]" : String(result);
log.push(`[[Get]](${String(n)}) -> ${str}`);
return result;
},
set(t, n, v) {
var result = Reflect.set(t, n, v);
log.push(`[[Set]](${String(n)}, ${String(v)}) -> ${String(result)}`);
return result;
},
has() { assertUnreachable("trap 'has' invoked"); },
deleteProperty() { assertUnreachable("trap 'deleteProperty' invoked"); },
ownKeys() { assertUnreachable("trap 'ownKeys' invoked"); },
apply() { assertUnreachable("trap 'apply' invoked"); },
construct() { assertUnreachable("trap 'construct' invoked"); },
getPrototypeOf() { assertUnreachable("trap 'getPrototypeOf' invoked"); },
setPrototypeOf() { assertUnreachable("trap 'setPrototypeOf' invoked"); },
isExtensible() { assertUnreachable("trap 'isExtensible' invoked"); },
preventExtensions() {
assertUnreachable("trap 'preventExtensions' invoked"); },
getOwnPropertyDescriptor() {
assertUnreachable("trap 'getOwnPropertyDescriptor' invoked"); },
defineProperty() { assertUnreachable("trap 'defineProperty' invoked"); },
};
var P = new Proxy({ x: 2 }, handler);
assertEquals(256, P.x **= "8");
assertEquals([
"[[Get]](x) -> 2",
"[[Set]](x, 256) -> true"
], log);
log = [];
var O = new Proxy({ p: P }, handler);
assertEquals(65536, O.p.x **= 2 );
assertEquals([
"[[Get]](p) -> [object Object]",
"[[Get]](x) -> 256",
"[[Set]](x, 65536) -> true"
], log);
}
TestOperationOrder();
function TestOverrideMathPow() {
var MathPow = MathPow;
Math.pow = function(a, b) {
assertUnreachable(`Math.pow(${String(a)}, ${String(b)}) invoked`);
}
TestBasic();
TestAssignment();
TestInvariants();
TestOperationOrder();
Math.pow = MathPow;
}
TestOverrideMathPow();
function TestBadAssignmentLHS() {
assertThrows("if (false) { 17 **= 10; }", ReferenceError);
assertThrows("if (false) { '17' **= 10; }", ReferenceError);
assertThrows("if (false) { /17/ **= 10; }", ReferenceError);
assertThrows("if (false) { ({ valueOf() { return 17; } } **= 10); }",
ReferenceError);
// TODO(caitp): a Call expression as LHS should be an early ReferenceError!
// assertThrows("if (false) { Array() **= 10; }", ReferenceError);
assertThrows(() => Array() **= 10, ReferenceError);
}
TestBadAssignmentLHS();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment