Browse Source

Add support for ES7 exponentiation operator ('**')

This avoids the need for a function call and up to two property lookups
for a `Math.pow()` invocation, as well as allowing expressions like
`2 ** 16` to be inlined at compile time.

Exponentiation uses the same internal handler as `Math.pow()`, per the
ES7 specification.

Usage:
x = base ** exp;
x **= 2;
pull/987/head
Bruce Pascoe 8 years ago
parent
commit
81ed33df24
  1. 68
      src-input/duk_bi_math.c
  2. 3
      src-input/duk_js.h
  3. 70
      src-input/duk_js_arith.c
  4. 9
      src-input/duk_js_bytecode.h
  5. 31
      src-input/duk_js_compiler.c
  6. 34
      src-input/duk_js_executor.c
  7. 6
      src-input/duk_lexer.c
  8. 62
      src-input/duk_lexer.h
  9. 1
      tools/configure.py
  10. 1
      util/dist.py

68
src-input/duk_bi_math.c

@ -127,70 +127,6 @@ DUK_LOCAL double duk__round_fixed(double x) {
return DUK_FLOOR(x + 0.5);
}
DUK_LOCAL double duk__pow_fixed(double x, double y) {
/* The ANSI C pow() semantics differ from Ecmascript.
*
* E.g. when x==1 and y is +/- infinite, the Ecmascript required
* result is NaN, while at least Linux pow() returns 1.
*/
duk_small_int_t cx, cy, sx;
DUK_UNREF(cx);
DUK_UNREF(sx);
cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
if (cy == DUK_FP_NAN) {
goto ret_nan;
}
if (DUK_FABS(x) == 1.0 && cy == DUK_FP_INFINITE) {
goto ret_nan;
}
#if defined(DUK_USE_POW_NETBSD_WORKAROUND)
/* See test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least) does not
* correctly handle some cases where x=+/-0. Specific fixes to these
* here.
*/
cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (cx == DUK_FP_ZERO && y < 0.0) {
sx = (duk_small_int_t) DUK_SIGNBIT(x);
if (sx == 0) {
/* Math.pow(+0,y) should be Infinity when y<0. NetBSD pow()
* returns -Infinity instead when y is <0 and finite. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
return DUK_DOUBLE_INFINITY;
} else {
/* Math.pow(-0,y) where y<0 should be:
* - -Infinity if y<0 and an odd integer
* - Infinity otherwise
* NetBSD pow() returns -Infinity for all finite y<0. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
/* fmod() return value has same sign as input (negative) so
* the result here will be in the range ]-2,0], 1 indicates
* odd. If x is -Infinity, NaN is returned and the odd check
* always concludes "not odd" which results in desired outcome.
*/
double tmp = DUK_FMOD(y, 2);
if (tmp == -1.0) {
return -DUK_DOUBLE_INFINITY;
} else {
/* Not odd, or y == -Infinity */
return DUK_DOUBLE_INFINITY;
}
}
}
#endif
return DUK_POW(x, y);
ret_nan:
return DUK_DOUBLE_NAN;
}
/* Wrappers for calling standard math library methods. These may be required
* on platforms where one or more of the math built-ins are defined as macros
* or inline functions and are thus not suitable to be used as function pointers.
@ -274,10 +210,10 @@ DUK_LOCAL const duk__one_arg_func duk__one_arg_funcs[] = {
DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = {
#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
duk__atan2,
duk__pow_fixed
duk_js_arith_pow
#else
DUK_ATAN2,
duk__pow_fixed
duk_js_arith_pow
#endif
};

3
src-input/duk_js.h

@ -41,6 +41,9 @@ DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x,
DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);
/* arithmetic */
DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y);
#define duk_js_equals(thr,tv_x,tv_y) \
duk_js_equals_helper((thr), (tv_x), (tv_y), 0)
#define duk_js_strict_equals(tv_x,tv_y) \

70
src-input/duk_js_arith.c

@ -0,0 +1,70 @@
/*
* Shared helpers for arithmetic operations
*/
#include "duk_internal.h"
/* Shared helper for Math.pow() and exponentiation operator */
DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
/* The ANSI C pow() semantics differ from Ecmascript.
*
* E.g. when x==1 and y is +/- infinite, the Ecmascript required
* result is NaN, while at least Linux pow() returns 1.
*/
duk_small_int_t cx, cy, sx;
DUK_UNREF(cx);
DUK_UNREF(sx);
cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
if (cy == DUK_FP_NAN) {
goto ret_nan;
}
if (DUK_FABS(x) == 1.0 && cy == DUK_FP_INFINITE) {
goto ret_nan;
}
#if defined(DUK_USE_POW_NETBSD_WORKAROUND)
/* See test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least) does not
* correctly handle some cases where x=+/-0. Specific fixes to these
* here.
*/
cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (cx == DUK_FP_ZERO && y < 0.0) {
sx = (duk_small_int_t) DUK_SIGNBIT(x);
if (sx == 0) {
/* Math.pow(+0,y) should be Infinity when y<0. NetBSD pow()
* returns -Infinity instead when y is <0 and finite. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
return DUK_DOUBLE_INFINITY;
} else {
/* Math.pow(-0,y) where y<0 should be:
* - -Infinity if y<0 and an odd integer
* - Infinity otherwise
* NetBSD pow() returns -Infinity for all finite y<0. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
/* fmod() return value has same sign as input (negative) so
* the result here will be in the range ]-2,0], 1 indicates
* odd. If x is -Infinity, NaN is returned and the odd check
* always concludes "not odd" which results in desired outcome.
*/
double tmp = DUK_FMOD(y, 2);
if (tmp == -1.0) {
return -DUK_DOUBLE_INFINITY;
} else {
/* Not odd, or y == -Infinity */
return DUK_DOUBLE_INFINITY;
}
}
}
#endif
return DUK_POW(x, y);
ret_nan:
return DUK_DOUBLE_NAN;
}

9
src-input/duk_js_bytecode.h

@ -384,10 +384,11 @@ typedef duk_uint32_t duk_instr_t;
#define DUK_OP_INVALID 189
#define DUK_OP_UNUSED190 190
#define DUK_OP_UNUSED191 191
#define DUK_OP_UNUSED192 192
#define DUK_OP_UNUSED193 193
#define DUK_OP_UNUSED194 194
#define DUK_OP_UNUSED195 195
#define DUK_OP_EXP 192
#define DUK_OP_EXP_RR 192
#define DUK_OP_EXP_CR 193
#define DUK_OP_EXP_RC 194
#define DUK_OP_EXP_CC 195
#define DUK_OP_UNUSED196 196
#define DUK_OP_UNUSED197 197
#define DUK_OP_UNUSED198 198

31
src-input/duk_js_compiler.c

@ -267,9 +267,10 @@ DUK_LOCAL_DECL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, d
#define DUK__BP_SHIFT 26
#define DUK__BP_ADDITIVE 28
#define DUK__BP_MULTIPLICATIVE 30
#define DUK__BP_POSTFIX 32
#define DUK__BP_CALL 34
#define DUK__BP_MEMBER 36
#define DUK__BP_EXPONENTIATION 32
#define DUK__BP_POSTFIX 34
#define DUK__BP_CALL 36
#define DUK__BP_MEMBER 38
#define DUK__TOKEN_LBP_BP_MASK 0x1f
#define DUK__TOKEN_LBP_FLAG_NO_REGEXP (1 << 5) /* regexp literal must not follow this token */
@ -353,6 +354,7 @@ DUK_LOCAL const duk_uint8_t duk__token_lbp[] = {
DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MUL */
DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_DIV */
DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MOD */
DUK__MK_LBP(DUK__BP_EXPONENTIATION), /* DUK_TOK_EXP */
DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_INCREMENT */
DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_DECREMENT */
DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ALSHIFT */
@ -373,6 +375,7 @@ DUK_LOCAL const duk_uint8_t duk__token_lbp[] = {
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MUL_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_DIV_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MOD_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_EXP_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ALSHIFT_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ARSHIFT_EQ */
DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_RSHIFT_EQ */
@ -2179,6 +2182,10 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x
case DUK_OP_SUB: d3 = d1 - d2; break;
case DUK_OP_MUL: d3 = d1 * d2; break;
case DUK_OP_DIV: d3 = d1 / d2; break;
case DUK_OP_EXP: {
d3 = (duk_double_t) duk_js_arith_pow((double) d1, (double) d2);
break;
}
default: accept = 0; break;
}
@ -3941,18 +3948,25 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
goto postincdec;
}
/* EXPONENTIATION EXPRESSION */
case DUK_TOK_EXP: {
args = (DUK_OP_EXP << 8) + DUK__BP_EXPONENTIATION - 1; /* UnaryExpression */
goto binary;
}
/* MULTIPLICATIVE EXPRESSION */
case DUK_TOK_MUL: {
args = (DUK_OP_MUL << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */
args = (DUK_OP_MUL << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */
goto binary;
}
case DUK_TOK_DIV: {
args = (DUK_OP_DIV << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */
args = (DUK_OP_DIV << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */
goto binary;
}
case DUK_TOK_MOD: {
args = (DUK_OP_MOD << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */
args = (DUK_OP_MOD << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */
goto binary;
}
@ -4130,6 +4144,11 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
args = (DUK_OP_MOD << 8) + DUK__BP_ASSIGNMENT - 1;
goto assign;
}
case DUK_TOK_EXP_EQ: {
/* right associative */
args = (DUK_OP_EXP << 8) + DUK__BP_ASSIGNMENT - 1;
goto assign;
}
case DUK_TOK_ALSHIFT_EQ: {
/* right associative */
args = (DUK_OP_BASL << 8) + DUK__BP_ASSIGNMENT - 1;

34
src-input/duk_js_executor.c

@ -84,6 +84,10 @@ DUK_LOCAL DUK__INLINE_PERF duk_double_t duk__compute_mod(duk_double_t d1, duk_do
return (duk_double_t) DUK_FMOD((double) d1, (double) d2);
}
DUK_LOCAL DUK__INLINE_PERF duk_double_t duk__compute_exp(duk_double_t d1, duk_double_t d2) {
return (duk_double_t) duk_js_arith_pow((double) d1, (double) d2);
}
DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_fast_t idx_z) {
/*
* Addition operator is different from other arithmetic
@ -321,6 +325,10 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv
du.d = duk__compute_mod(d1, d2);
break;
}
case DUK_OP_EXP >> 2: {
du.d = duk__compute_exp(d1, d2);
break;
}
default: {
DUK_UNREACHABLE();
du.d = DUK_DOUBLE_NAN; /* should not happen */
@ -3143,7 +3151,11 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
case DUK_OP_MOD_RR:
case DUK_OP_MOD_CR:
case DUK_OP_MOD_RC:
case DUK_OP_MOD_CC: {
case DUK_OP_MOD_CC:
case DUK_OP_EXP_RR:
case DUK_OP_EXP_CR:
case DUK_OP_EXP_RC:
case DUK_OP_EXP_CC: {
/* XXX: could leave value on stack top and goto replace_top_a; */
duk__vm_arith_binary_op(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins), op);
break;
@ -3213,6 +3225,22 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD);
break;
}
case DUK_OP_EXP_RR: {
duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
break;
}
case DUK_OP_EXP_CR: {
duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
break;
}
case DUK_OP_EXP_RC: {
duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
break;
}
case DUK_OP_EXP_CC: {
duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
break;
}
#endif /* DUK_USE_EXEC_PREFER_SIZE */
#if defined(DUK_USE_EXEC_PREFER_SIZE)
@ -4895,8 +4923,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
}
#if !defined(DUK_USE_EXEC_PREFER_SIZE)
case DUK_OP_UNUSED194:
case DUK_OP_UNUSED195:
case DUK_OP_UNUSED190:
case DUK_OP_UNUSED191:
case DUK_OP_UNUSED196:
case DUK_OP_UNUSED197:
case DUK_OP_UNUSED198:

6
src-input/duk_lexer.c

@ -1035,7 +1035,11 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
}
break;
case DUK_ASC_STAR: /* '*' */
if (DUK__L1() == '=') {
if (DUK__L1() == '*' && DUK__L2() == '=') {
advtok = DUK__ADVTOK(3, DUK_TOK_EXP_EQ);
} else if (DUK__L1() == '*') {
advtok = DUK__ADVTOK(2, DUK_TOK_EXP);
} else if (DUK__L1() == '=') {
advtok = DUK__ADVTOK(2, DUK_TOK_MUL_EQ);
} else {
advtok = DUK__ADVTOK(1, DUK_TOK_MUL);

62
src-input/duk_lexer.h

@ -135,41 +135,43 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo
#define DUK_TOK_MUL 68
#define DUK_TOK_DIV 69
#define DUK_TOK_MOD 70
#define DUK_TOK_INCREMENT 71
#define DUK_TOK_DECREMENT 72
#define DUK_TOK_ALSHIFT 73 /* named "arithmetic" because result is signed */
#define DUK_TOK_ARSHIFT 74
#define DUK_TOK_RSHIFT 75
#define DUK_TOK_BAND 76
#define DUK_TOK_BOR 77
#define DUK_TOK_BXOR 78
#define DUK_TOK_LNOT 79
#define DUK_TOK_BNOT 80
#define DUK_TOK_LAND 81
#define DUK_TOK_LOR 82
#define DUK_TOK_QUESTION 83
#define DUK_TOK_COLON 84
#define DUK_TOK_EQUALSIGN 85
#define DUK_TOK_ADD_EQ 86
#define DUK_TOK_SUB_EQ 87
#define DUK_TOK_MUL_EQ 88
#define DUK_TOK_DIV_EQ 89
#define DUK_TOK_MOD_EQ 90
#define DUK_TOK_ALSHIFT_EQ 91
#define DUK_TOK_ARSHIFT_EQ 92
#define DUK_TOK_RSHIFT_EQ 93
#define DUK_TOK_BAND_EQ 94
#define DUK_TOK_BOR_EQ 95
#define DUK_TOK_BXOR_EQ 96
#define DUK_TOK_EXP 71
#define DUK_TOK_INCREMENT 72
#define DUK_TOK_DECREMENT 73
#define DUK_TOK_ALSHIFT 74 /* named "arithmetic" because result is signed */
#define DUK_TOK_ARSHIFT 75
#define DUK_TOK_RSHIFT 76
#define DUK_TOK_BAND 77
#define DUK_TOK_BOR 78
#define DUK_TOK_BXOR 79
#define DUK_TOK_LNOT 80
#define DUK_TOK_BNOT 81
#define DUK_TOK_LAND 82
#define DUK_TOK_LOR 83
#define DUK_TOK_QUESTION 84
#define DUK_TOK_COLON 85
#define DUK_TOK_EQUALSIGN 86
#define DUK_TOK_ADD_EQ 87
#define DUK_TOK_SUB_EQ 88
#define DUK_TOK_MUL_EQ 89
#define DUK_TOK_DIV_EQ 90
#define DUK_TOK_MOD_EQ 91
#define DUK_TOK_EXP_EQ 92
#define DUK_TOK_ALSHIFT_EQ 93
#define DUK_TOK_ARSHIFT_EQ 94
#define DUK_TOK_RSHIFT_EQ 95
#define DUK_TOK_BAND_EQ 96
#define DUK_TOK_BOR_EQ 97
#define DUK_TOK_BXOR_EQ 98
/* literals (E5 Section 7.8), except null, true, false, which are treated
* like reserved words (above).
*/
#define DUK_TOK_NUMBER 97
#define DUK_TOK_STRING 98
#define DUK_TOK_REGEXP 99
#define DUK_TOK_NUMBER 99
#define DUK_TOK_STRING 100
#define DUK_TOK_REGEXP 101
#define DUK_TOK_MAXVAL 99 /* inclusive */
#define DUK_TOK_MAXVAL 101 /* inclusive */
/* Convert heap string index to a token (reserved words) */
#define DUK_STRIDX_TO_TOK(x) ((x) - DUK_STRIDX_START_RESERVED + DUK_TOK_START_RESERVED)

1
tools/configure.py

@ -475,6 +475,7 @@ def main():
'duk_internal.h',
'duk_jmpbuf.h',
'duk_exception.h',
'duk_js_arith.c',
'duk_js_bytecode.h',
'duk_js_call.c',
'duk_js_compiler.c',

1
util/dist.py

@ -385,6 +385,7 @@ def main():
'duk_hthread_stacks.c',
'duk_internal.h',
'duk_jmpbuf.h',
'duk_js_arith.c',
'duk_js_bytecode.h',
'duk_js_call.c',
'duk_js_compiler.c',

Loading…
Cancel
Save