diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 18:57:11 +0200 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 18:57:11 +0200 |
commit | 0e8fffd4de4a10f498f46cd0e99f53da6a523542 (patch) | |
tree | fc80c9b15e5816b84ae7d012a1106f7f46a70332 | |
parent | 91459fb6723e29e923380cec0023af93819ae69d (diff) | |
download | quickjs-0e8fffd4de4a10f498f46cd0e99f53da6a523542.tar.gz quickjs-0e8fffd4de4a10f498f46cd0e99f53da6a523542.zip |
2020-01-19 release
-rw-r--r-- | Changelog | 12 | ||||
-rw-r--r-- | Makefile | 34 | ||||
-rw-r--r-- | TODO | 5 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | doc/jsbignum.html | 754 | ||||
-rw-r--r-- | doc/jsbignum.pdf | bin | 199698 -> 153319 bytes | |||
-rw-r--r-- | doc/jsbignum.texi | 601 | ||||
-rw-r--r-- | doc/quickjs.html | 15 | ||||
-rw-r--r-- | doc/quickjs.pdf | bin | 162866 -> 162954 bytes | |||
-rw-r--r-- | doc/quickjs.texi | 9 | ||||
-rw-r--r-- | examples/pi_bigdecimal.js | 18 | ||||
-rw-r--r-- | libbf.c | 1116 | ||||
-rw-r--r-- | libbf.h | 119 | ||||
-rw-r--r-- | qjs.c | 26 | ||||
-rw-r--r-- | qjsc.c | 24 | ||||
-rw-r--r-- | qjscalc.js | 1435 | ||||
-rw-r--r-- | quickjs-atom.h | 26 | ||||
-rw-r--r-- | quickjs-libc.c | 15 | ||||
-rw-r--r-- | quickjs-opcode.h | 3 | ||||
-rw-r--r-- | quickjs.c | 3206 | ||||
-rw-r--r-- | quickjs.h | 16 | ||||
-rw-r--r-- | repl.js | 34 | ||||
-rw-r--r-- | test262_errors.txt | 13 | ||||
-rw-r--r-- | tests/test_bignum.js | 165 | ||||
-rw-r--r-- | tests/test_op_overloading.js | 207 | ||||
-rw-r--r-- | tests/test_qjscalc.js | 18 |
26 files changed, 4266 insertions, 3607 deletions
@@ -1,3 +1,15 @@ +2020-01-19: + +- keep CONFIG_BIGNUM in the makefile +- added os.chdir() +- qjs: added -I option +- more memory checks in the bignum operations +- modified operator overloading semantics to be closer to the TC39 + proposal +- suppressed "use bigint" mode. Simplified "use math" mode +- BigDecimal: changed suffix from 'd' to 'm' +- misc bug fixes + 2020-01-05: - always compile the bignum code. Added '--bignum' option to qjs. @@ -1,8 +1,8 @@ # # QuickJS Javascript Engine # -# Copyright (c) 2017-2019 Fabrice Bellard -# Copyright (c) 2017-2019 Charlie Gordon +# Copyright (c) 2017-2020 Fabrice Bellard +# Copyright (c) 2017-2020 Charlie Gordon # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -47,6 +47,8 @@ prefix=/usr/local #CONFIG_PROFILE=y # use address sanitizer #CONFIG_ASAN=y +# include the code for BigInt/BigFloat/BigDecimal and math mode +CONFIG_BIGNUM=y OBJDIR=.obj @@ -94,6 +96,9 @@ ifdef CONFIG_WERROR CFLAGS+=-Werror endif DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" +ifdef CONFIG_BIGNUM +DEFINES+=-DCONFIG_BIGNUM +endif CFLAGS+=$(DEFINES) CFLAGS_DEBUG=$(CFLAGS) -O0 CFLAGS_SMALL=$(CFLAGS) -Os @@ -153,9 +158,13 @@ endif all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) -QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/libbf.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o +QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o -QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(OBJDIR)/qjscalc.o $(QJS_LIB_OBJS) +QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) +ifdef CONFIG_BIGNUM +QJS_LIB_OBJS+=$(OBJDIR)/libbf.o +QJS_OBJS+=$(OBJDIR)/qjscalc.o +endif LIBS=-lm ifndef CONFIG_WIN32 @@ -215,7 +224,7 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) $(AR) rcs $@ $^ endif # CONFIG_LTO -repl.c: $(QJSC) repl.js +repl.c: $(QJSC) repl.js $(QJSC) -c -o $@ -m repl.js qjscalc.c: $(QJSC) qjscalc.js @@ -301,7 +310,10 @@ endif HELLO_SRCS=examples/hello.js HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ - -fno-date -fno-module-loader -fno-bigint + -fno-date -fno-module-loader +ifdef CONFIG_BIGNUM +HELLO_OPTS+=-fno-bigint +endif hello.c: $(QJSC) $(HELLO_SRCS) $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) @@ -372,20 +384,30 @@ test: qjs ./qjs tests/test_loop.js ./qjs tests/test_std.js ifndef CONFIG_DARWIN +ifdef CONFIG_BIGNUM ./qjs --bignum tests/test_bjson.js +else + ./qjs tests/test_bjson.js +endif ./qjs examples/test_point.js endif +ifdef CONFIG_BIGNUM + ./qjs --bignum tests/test_op_overloading.js ./qjs --bignum tests/test_bignum.js ./qjs --qjscalc tests/test_qjscalc.js +endif ifdef CONFIG_M32 ./qjs32 tests/test_closure.js ./qjs32 tests/test_op.js ./qjs32 tests/test_builtin.js ./qjs32 tests/test_loop.js ./qjs32 tests/test_std.js +ifdef CONFIG_BIGNUM + ./qjs32 --bignum tests/test_op_overloading.js ./qjs32 --bignum tests/test_bignum.js ./qjs32 --qjscalc tests/test_qjscalc.js endif +endif stats: qjs qjs32 ./qjs -qd @@ -73,6 +73,5 @@ REPL: Test262o: 0/11262 errors, 463 excluded Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) -Test262: 4/67619 errors, 913 excluded, 1660 skipped -Test262bn: 4/69722 errors, 846 excluded, 672 skipped -test262 commit: 19fd4bea797646ae9bbfc9d325f14052ca370b54 +Test262: 17/69942 errors, 855 excluded, 581 skipped +test262 commit: 28b4fcca4b1b1d278dfe0cc0e69c7d9d59b31aab @@ -1 +1 @@ -2020-01-05 +2020-01-19 diff --git a/doc/jsbignum.html b/doc/jsbignum.html index c60dc45..096dc04 100644 --- a/doc/jsbignum.html +++ b/doc/jsbignum.html @@ -1,7 +1,8 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> -<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ --> +<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ --> <head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Javascript Bignum Extensions</title> <meta name="description" content="Javascript Bignum Extensions"> @@ -9,7 +10,6 @@ <meta name="resource-type" content="document"> <meta name="distribution" content="global"> <meta name="Generator" content="makeinfo"> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link href="#SEC_Contents" rel="contents" title="Table of Contents"> <style type="text/css"> <!-- @@ -52,61 +52,33 @@ ul.no-bullet {list-style: none} <div class="contents"> <ul class="no-bullet"> <li><a name="toc-Introduction" href="#Introduction">1 Introduction</a></li> -<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a> +<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a></li> +<li><a name="toc-BigInt-extensions" href="#BigInt-extensions">3 BigInt extensions</a></li> +<li><a name="toc-BigFloat" href="#BigFloat">4 BigFloat</a> <ul class="no-bullet"> - <li><a name="toc-Introduction-1" href="#Introduction-1">2.1 Introduction</a></li> - <li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">2.2 Builtin Object changes</a> - <ul class="no-bullet"> - <li><a name="toc-Symbol-constructor" href="#Symbol-constructor">2.2.1 <code>Symbol</code> constructor</a></li> - </ul></li> -</ul></li> -<li><a name="toc-The-BigInt-Mode" href="#The-BigInt-Mode">3 The BigInt Mode</a> -<ul class="no-bullet"> - <li><a name="toc-Introduction-2" href="#Introduction-2">3.1 Introduction</a></li> - <li><a name="toc-Changes-that-introduce-incompatibilities-with-Javascript" href="#Changes-that-introduce-incompatibilities-with-Javascript">3.2 Changes that introduce incompatibilities with Javascript</a> - <ul class="no-bullet"> - <li><a name="toc-Standard-mode" href="#Standard-mode">3.2.1 Standard mode</a></li> - <li><a name="toc-Bigint-mode" href="#Bigint-mode">3.2.2 Bigint mode</a></li> - </ul></li> - <li><a name="toc-Operators" href="#Operators">3.3 Operators</a> - <ul class="no-bullet"> - <li><a name="toc-Arithmetic-operators" href="#Arithmetic-operators">3.3.1 Arithmetic operators</a></li> - <li><a name="toc-Logical-operators" href="#Logical-operators">3.3.2 Logical operators</a></li> - <li><a name="toc-Relational-operators" href="#Relational-operators">3.3.3 Relational operators</a></li> - </ul></li> - <li><a name="toc-Number-literals" href="#Number-literals">3.4 Number literals</a></li> - <li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">3.5 Builtin Object changes</a> - <ul class="no-bullet"> - <li><a name="toc-BigInt-function" href="#BigInt-function">3.5.1 <code>BigInt</code> function</a></li> - <li><a name="toc-BigInt_002eprototype" href="#BigInt_002eprototype">3.5.2 <code>BigInt.prototype</code></a></li> - <li><a name="toc-Number-constructor" href="#Number-constructor">3.5.3 <code>Number</code> constructor</a></li> - <li><a name="toc-Number_002eprototype" href="#Number_002eprototype">3.5.4 <code>Number.prototype</code></a></li> - <li><a name="toc-Math-object" href="#Math-object">3.5.5 <code>Math</code> object</a></li> - </ul></li> -</ul></li> -<li><a name="toc-Arbitrarily-large-floating-point-numbers" href="#Arbitrarily-large-floating-point-numbers">4 Arbitrarily large floating point numbers</a> -<ul class="no-bullet"> - <li><a name="toc-Introduction-3" href="#Introduction-3">4.1 Introduction</a></li> + <li><a name="toc-Introduction-1" href="#Introduction-1">4.1 Introduction</a></li> <li><a name="toc-Floating-point-rounding" href="#Floating-point-rounding">4.2 Floating point rounding</a></li> - <li><a name="toc-Operators-1" href="#Operators-1">4.3 Operators</a></li> + <li><a name="toc-Operators" href="#Operators">4.3 Operators</a></li> <li><a name="toc-BigFloat-literals" href="#BigFloat-literals">4.4 BigFloat literals</a></li> - <li><a name="toc-Builtin-Object-changes-2" href="#Builtin-Object-changes-2">4.5 Builtin Object changes</a> + <li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">4.5 Builtin Object changes</a> <ul class="no-bullet"> <li><a name="toc-BigFloat-function" href="#BigFloat-function">4.5.1 <code>BigFloat</code> function</a></li> <li><a name="toc-BigFloat_002eprototype" href="#BigFloat_002eprototype">4.5.2 <code>BigFloat.prototype</code></a></li> <li><a name="toc-BigFloatEnv-constructor" href="#BigFloatEnv-constructor">4.5.3 <code>BigFloatEnv</code> constructor</a></li> - <li><a name="toc-Math-object-1" href="#Math-object-1">4.5.4 <code>Math</code> object</a></li> </ul></li> </ul></li> -<li><a name="toc-Math-mode" href="#Math-mode">5 Math mode</a> +<li><a name="toc-BigDecimal" href="#BigDecimal">5 BigDecimal</a> <ul class="no-bullet"> - <li><a name="toc-Introduction-4" href="#Introduction-4">5.1 Introduction</a></li> - <li><a name="toc-Builtin-Object-changes-3" href="#Builtin-Object-changes-3">5.2 Builtin Object changes</a> + <li><a name="toc-Operators-1" href="#Operators-1">5.1 Operators</a></li> + <li><a name="toc-BigDecimal-literals" href="#BigDecimal-literals">5.2 BigDecimal literals</a></li> + <li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">5.3 Builtin Object changes</a> <ul class="no-bullet"> - <li><a name="toc-Symbol-constructor-1" href="#Symbol-constructor-1">5.2.1 <code>Symbol</code> constructor</a></li> + <li><a name="toc-The-BigDecimal-function_002e" href="#The-BigDecimal-function_002e">5.3.1 The <code>BigDecimal</code> function.</a></li> + <li><a name="toc-Properties-of-the-BigDecimal-object" href="#Properties-of-the-BigDecimal-object">5.3.2 Properties of the <code>BigDecimal</code> object</a></li> + <li><a name="toc-Properties-of-the-BigDecimal_002eprototype-object" href="#Properties-of-the-BigDecimal_002eprototype-object">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</a></li> </ul></li> - <li><a name="toc-Remaining-issues" href="#Remaining-issues">5.3 Remaining issues</a></li> </ul></li> +<li><a name="toc-Math-mode" href="#Math-mode">6 Math mode</a></li> </ul> </div> @@ -119,379 +91,52 @@ ul.no-bullet {list-style: none} language while being 100% backward compatible: </p> <ul> -<li> Overloading of the standard operators -to support new types such as complex numbers, fractions or matrices. - -</li><li> Bigint mode where arbitrarily large integers are available by default (no <code>n</code> suffix is necessary as in the TC39 BigInt proposal<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>). +<li> Operator overloading with a dispatch logic inspired from the proposal available at <a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. </li><li> Arbitrarily large floating point numbers (<code>BigFloat</code>) in base 2 using the IEEE 754 semantics. -</li><li> Optional <code>math</code> mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder. +</li><li> Arbitrarily large floating point numbers (<code>BigDecimal</code>) in base 10 based on the proposal available at +<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>. + +</li><li> <code>math</code> mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (<code>%</code>) is defined as the Euclidian +remainder. <code>^</code> is an alias to the power operator +(<code>**</code>). <code>^^</code> is used as the exclusive or operator. </li></ul> <p>The extensions are independent from each other except the <code>math</code> -mode which relies on the bigint mode and the operator overloading. +mode which relies on BigFloat and operator overloading. </p> <a name="Operator-overloading"></a> <h2 class="chapter">2 Operator overloading</h2> -<a name="Introduction-1"></a> -<h3 class="section">2.1 Introduction</h3> - -<p>If the operands of an operator have at least one object type, a custom -operator method is searched before doing the legacy Javascript -<code>ToNumber</code> conversion. -</p> -<p>For unary operators, the custom function is looked up in the object -and has the following name: -</p> -<dl compact="compact"> -<dt><code>unary +</code></dt> -<dd><p><code>Symbol.operatorPlus</code> -</p> -</dd> -<dt><code>unary -</code></dt> -<dd><p><code>Symbol.operatorNeg</code> -</p> -</dd> -<dt><code>++</code></dt> -<dd><p><code>Symbol.operatorInc</code> -</p> -</dd> -<dt><code>--</code></dt> -<dd><p><code>Symbol.operatorDec</code> -</p> -</dd> -<dt><code>~</code></dt> -<dd><p><code>Symbol.operatorNot</code> -</p> -</dd> -</dl> - -<p>For binary operators: -</p> -<ul> -<li> If both operands have the same constructor function, then the operator -is looked up in the constructor. - -</li><li> Otherwise, the property <code>Symbol.operatorOrder</code> is looked up in both -constructors and converted to <code>Int32</code>. The operator is then -looked in the constructor with the larger <code>Symbol.operatorOrder</code> -value. A <code>TypeError</code> is raised if both constructors have the same -<code>Symbol.operatorOrder</code> value. - -</li></ul> - -<p>The operator is looked up with the following name: -</p> -<dl compact="compact"> -<dt><code>+</code></dt> -<dd><p><code>Symbol.operatorAdd</code> -</p> -</dd> -<dt><code>-</code></dt> -<dd><p><code>Symbol.operatorSub</code> -</p> -</dd> -<dt><code>*</code></dt> -<dd><p><code>Symbol.operatorMul</code> -</p> -</dd> -<dt><code>/</code></dt> -<dd><p><code>Symbol.operatorDiv</code> -</p> -</dd> -<dt><code>%</code></dt> -<dd><p><code>Symbol.operatorMod</code> -</p> -</dd> -<dt><code>% (math mode)</code></dt> -<dd><p><code>Symbol.operatorMathMod</code> -</p> -</dd> -<dt><code>**</code></dt> -<dd><p><code>Symbol.operatorPow</code> -</p> -</dd> -<dt><code>|</code></dt> -<dd><p><code>Symbol.operatorOr</code> -</p> -</dd> -<dt><code>^</code></dt> -<dd><p><code>Symbol.operatorXor</code> -</p> -</dd> -<dt><code>&</code></dt> -<dd><p><code>Symbol.operatorAnd</code> -</p> -</dd> -<dt><code><<</code></dt> -<dd><p><code>Symbol.operatorShl</code> -</p> -</dd> -<dt><code>>></code></dt> -<dd><p><code>Symbol.operatorShr</code> -</p> -</dd> -<dt><code><</code></dt> -<dd><p><code>Symbol.operatorCmpLT</code> -</p> -</dd> -<dt><code>></code></dt> -<dd><p><code>Symbol.operatorCmpLT</code>, operands swapped -</p> -</dd> -<dt><code><=</code></dt> -<dd><p><code>Symbol.operatorCmpLE</code> -</p> -</dd> -<dt><code>>=</code></dt> -<dd><p><code>Symbol.operatorCmpLE</code>, operands swapped -</p> -</dd> -<dt><code>==, !=</code></dt> -<dd><p><code>Symbol.operatorCmpEQ</code> -</p> -</dd> -</dl> - -<p>The return value of <code>Symbol.operatorCmpLT</code>, <code>Symbol.operatorCmpLE</code> and -<code>Symbol.operatorCmpEQ</code> is converted to <code>Boolean</code>. +<p>Operator overloading is inspired from the proposal available at +<a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. It +implements the same dispatch logic but finds the operator sets by +looking at the <code>Symbol.operatorSet</code> property in the objects. The +changes were done in order to simplify the implementation. </p> -<a name="Builtin-Object-changes"></a> -<h3 class="section">2.2 Builtin Object changes</h3> - -<a name="Symbol-constructor"></a> -<h4 class="subsection">2.2.1 <code>Symbol</code> constructor</h4> - -<p>The following global symbols are added for the operator overloading: -</p><dl compact="compact"> -<dt><code>operatorOrder</code></dt> -<dt><code>operatorAdd</code></dt> -<dt><code>operatorSub</code></dt> -<dt><code>operatorMul</code></dt> -<dt><code>operatorDiv</code></dt> -<dt><code>operatorMod</code></dt> -<dt><code>operatorPow</code></dt> -<dt><code>operatorShl</code></dt> -<dt><code>operatorShr</code></dt> -<dt><code>operatorAnd</code></dt> -<dt><code>operatorOr</code></dt> -<dt><code>operatorXor</code></dt> -<dt><code>operatorCmpLT</code></dt> -<dt><code>operatorCmpLE</code></dt> -<dt><code>operatorCmpEQ</code></dt> -<dt><code>operatorPlus</code></dt> -<dt><code>operatorNeg</code></dt> -<dt><code>operatorNot</code></dt> -<dt><code>operatorInc</code></dt> -<dt><code>operatorDec</code></dt> -</dl> - - -<a name="The-BigInt-Mode"></a> -<h2 class="chapter">3 The BigInt Mode</h2> - -<a name="Introduction-2"></a> -<h3 class="section">3.1 Introduction</h3> - -<p>The bigint mode is enabled with the <code>"use bigint"</code> directive. It -propagates the same way as the strict mode. In bigint mode, all -integers are considered as <code>bigint</code> (arbitrarily large integer, -similar to the TC39 BigInt -proposal<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>) -instead of <code>number</code> (floating point number). In order to be able -to exchange data between standard and bigint modes, numbers are -internally represented as 3 different types: +<p>More precisely, the following modifications were made: </p> <ul> -<li> Small integer (SmallInt): 32 bit integer<a name="DOCF3" href="#FOOT3"><sup>3</sup></a>. +<li> <code>with operators from</code> is not supported. Operator overloading is always enabled. -</li><li> Big integer (BigInt): arbitrarily large integer. +</li><li> The dispatch is not based on a static <code>[[OperatorSet]]</code> field in all instances. Instead, a dynamic lookup the of the <code>Symbol.operatorSet</code> property is done. This property is typically added in the prototype of each object. -</li><li> Floating point number (Float). +</li><li> <code>Operators.create(...dictionaries)</code> is used to create a new OperatorSet object. The <code>Operators</code> function is supported as an helper to be closer to the TC39 proposal. -</li></ul> - -<p>In standard mode, the semantics of each operation is modified so that -when it returns a <code>number</code>, it is either of SmallInt or -Float. But the difference between SmallInt and Float is not observable -in standard mode. -</p> -<p>In bigint mode, each operation behaves differently whether its -operands are integer or float. The difference between SmallInt and -BigInt is not observable (i.e. they are both integers). -</p> -<p>The following table summarizes the observable types: -</p> -<table> -<thead><tr><th width="30%">Internal type</th><th width="30%">Observable type<br> (standard mode)</th><th width="30%">Observable type<br> (bigint mode)</th></tr></thead> -<tr><td width="30%">SmallInt</td><td width="30%">number</td><td width="30%">bigint</td></tr> -<tr><td width="30%">BigInt</td><td width="30%">bigint</td><td width="30%">bigint</td></tr> -<tr><td width="30%">Float</td><td width="30%">number</td><td width="30%">number</td></tr> -</table> - -<a name="Changes-that-introduce-incompatibilities-with-Javascript"></a> -<h3 class="section">3.2 Changes that introduce incompatibilities with Javascript</h3> +</li><li> <code>[]</code> cannot be overloaded. -<a name="Standard-mode"></a> -<h4 class="subsection">3.2.1 Standard mode</h4> - -<p>There is no incompatibility with Javascript. -</p> -<a name="Bigint-mode"></a> -<h4 class="subsection">3.2.2 Bigint mode</h4> - -<p>The following changes are visible: -</p> -<ul> -<li> Integer and Float are different types. Constants are typed. For example: <code>typeof 1.0 === "number"</code> and <code>typeof 1 === "bigint"</code>. Another consequence is that <code>1.0 === 1</code> is false. - -</li><li> The range of integers is unlimited. In standard mode: <code>2**53 + 1 === 2**53</code>. This is no longer true with the bignum extensions. - -</li><li> Binary bitwise operators do not truncate to 32 bits i.e. <code>0x800000000 | 1 === 0x800000001</code> while it gives <code>1</code> in standard mode. - -</li><li> Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with <code>0x1f</code> i.e. <code>1 << 32 === 4294967296</code> while it gives <code>1</code> in standard mode. However, the <code>>>></code> operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior<a name="DOCF4" href="#FOOT4"><sup>4</sup></a>. - -</li><li> Operators with integer operands never return the minus zero floating point value as result. Hence <code>Object.is(0, -0) === true</code>. Use <code>-0.0</code> to create a minus zero floating point value. - -</li><li> The <code>ToPrimitive</code> abstract operation is called with the <code>"integer"</code> preferred type when an integer is required (e.g. for bitwise binary or shift operations). - -</li><li> The prototype of integers is no longer <code>Number.prototype</code>. Instead<br> <code>Object.getPrototypeOf(1) === BigInt.prototype</code>. The prototype of floats remains Number.prototype. - -</li><li> If the TC39 BigInt proposal is supported, there is no observable difference between integers and <code>bigint</code>s. +</li><li> In math mode, the BigInt division and power operators can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>. </li></ul> -<a name="Operators"></a> -<h3 class="section">3.3 Operators</h3> - -<a name="Arithmetic-operators"></a> -<h4 class="subsection">3.3.1 Arithmetic operators</h4> - -<p>The operands are converted to number values as in normal -Javascript. Then the general case is that an Integer is returned if -both operands are Integer. Otherwise, a float is returned. -</p> -<p>The <code>+</code> operator also accepts strings as input and behaves like -standard Javascript in this case. -</p> -<p>The binary operator <code>%</code> returns the truncated remainder of the -division. When the result is an Integer type, a dividend of zero yields a -RangeError exception. -</p> -<p>The binary operator <code>%</code> in math mode returns the Euclidian -remainder of the division i.e. it is always positive. -</p> -<p>The binary operator <code>/</code> returns a float. -</p> -<p>The binary operator <code>/</code> in math mode returns a float if one of -the operands is float. Otherwise, <code>BigInt[Symbol.operatorDiv]</code> is -invoked. -</p> -<p>The returned type of <code>a ** b</code> is Float if <em>a</em> or <em>b</em> -are Float. If <em>a</em> and <em>b</em> are integers: -</p><ul> -<li> <em>b < 0</em> returns a Float in bigint mode. In math mode, <code>BigInt[Symbol.operatorPow]</code> is invoked. - -</li><li> <em>b >= 0</em> returns an integer. -</li></ul> - -<p>The unary <code>-</code> and unary <code>+</code> return the same type as their -operand. They performs no floating point rounding when the result is a -float. -</p> -<p>The unary operators <code>++</code> and <code>--</code> return the same type as -their operand. -</p> -<p>In standard mode: -</p> -<p>If the operator returns an Integer and that the result fits a -SmallInt, it is converted to SmallInt. Otherwise, the Integer is -converted to a Float. -</p> -<p>In bigint mode: -</p> -<p>If the operator returns an Integer and that the result fits a -SmallInt, it is converted to SmallInt. Otherwise it is a BigInt. -</p> -<a name="Logical-operators"></a> -<h4 class="subsection">3.3.2 Logical operators</h4> - -<p>In standard mode: -</p> -<p>The operands have their standard behavior. If the result fits a -SmallInt it is converted to a SmallInt. Otherwise it is a Float. -</p> -<p>In bigint mode: -</p> -<p>The operands are converted to integer values. The floating point -values are converted to integer by rounding them to zero. -</p> -<p>The logical operators are defined assuming the integers are -represented in two complement notation. -</p> -<p>For <code><<</code> and <code><<</code>, the shift can be positive or negative. So -<code>a << b</code> is defined as <em>\lfloor a/2^{-b} \rfloor</em> and -<code>a >> b</code> is defined as <em>\lfloor a/2^{b} \rfloor</em>. -</p> -<p>The operator <code>>>></code> is supported for backward compatibility and -behaves the same way as Javascript i.e. implicit conversion to <code>Uint32</code>. -</p> -<p>If the result fits a SmallInt it is converted to a SmallInt. Otherwise -it is a BigInt. -</p> -<a name="Relational-operators"></a> -<h4 class="subsection">3.3.3 Relational operators</h4> - -<p>The relational operators <, <=, >, >=, ==, != work as expected with -integers and floating point numbers (e.g. <code>1.0 == 1</code> is true). -</p> -<p>The strict equality operators === and !== have the usual Javascript -semantics. In particular, different types never equal, so <code>1.0 -=== 1</code> is false. -</p> -<a name="Number-literals"></a> -<h3 class="section">3.4 Number literals</h3> +<a name="BigInt-extensions"></a> +<h2 class="chapter">3 BigInt extensions</h2> -<p>Number literals in bigint mode have a slightly different behavior than -in standard Javascript: -</p> -<ol> -<li> A number literal without a decimal point or an exponent is considered -as an Integer. Otherwise it is a Float. - -</li><li> Hexadecimal, octal or binary floating point literals are accepted with -a decimal point or an exponent. The exponent is specified with the -<code>p</code> letter assuming a base 2. The same convention is used by -C99. Example: <code>0x1p3</code> is the same as <code>8.0</code>. - -</li></ol> - -<a name="Builtin-Object-changes-1"></a> -<h3 class="section">3.5 Builtin Object changes</h3> - -<a name="BigInt-function"></a> -<h4 class="subsection">3.5.1 <code>BigInt</code> function</h4> - -<p>The <code>BigInt</code> function cannot be invoked as a constructor. When -invoked as a function, it converts its first parameter to an -integer. When a floating point number is given as parameter, it is -truncated to an integer with infinite precision. -</p> -<p><code>BigInt</code> properties: +<p>A few properties are added to the BigInt object: </p> <dl compact="compact"> -<dt><code>asIntN(bits, a)</code></dt> -<dd><p>Set <em>b=a \pmod{2^{bits}}</em>. Return <em>b</em> if <em>b < 2^{bits-1}</em> -otherwise <em>b-2^{bits}</em>. -</p> -</dd> -<dt><code>asUintN(bits, a)</code></dt> -<dd><p>Return <em>a \pmod{2^{bits}}</em>. -</p> -</dd> <dt><code>tdiv(a, b)</code></dt> <dd><p>Return <em>trunc(a/b)</em>. <code>b = 0</code> raises a RangeError exception. @@ -542,70 +187,10 @@ raised if <em>a < 0</em>. </dd> </dl> -<a name="BigInt_002eprototype"></a> -<h4 class="subsection">3.5.2 <code>BigInt.prototype</code></h4> +<a name="BigFloat"></a> +<h2 class="chapter">4 BigFloat</h2> -<p>It is a normal object. -</p> -<a name="Number-constructor"></a> -<h4 class="subsection">3.5.3 <code>Number</code> constructor</h4> - -<p>The number constructor returns its argument rounded to a Float using -the global floating point environment. In bigint mode, the Number -constructor returns a Float. In standard mode, it returns a SmallInt -if the value fits it, otherwise a Float. -</p> -<a name="Number_002eprototype"></a> -<h4 class="subsection">3.5.4 <code>Number.prototype</code></h4> - -<p>The following properties are modified: -</p> -<dl compact="compact"> -<dt><code>toString(radix)</code></dt> -<dd> -<p>In bigint mode, integers are converted to the specified radix with -infinite precision. -</p> -</dd> -<dt><code>toPrecision(p)</code></dt> -<dt><code>toFixed(p)</code></dt> -<dt><code>toExponential(p)</code></dt> -<dd> -<p>In bigint mode, integers are accepted and converted to string with -infinite precision. -</p> -</dd> -<dt><code>parseInt(string, radix)</code></dt> -<dd> -<p>In bigint mode, an integer is returned and the conversion is done with -infinite precision. -</p> -</dd> -</dl> - -<a name="Math-object"></a> -<h4 class="subsection">3.5.5 <code>Math</code> object</h4> - -<p>The following properties are modified: -</p> -<dl compact="compact"> -<dt><code>abs(x)</code></dt> -<dd><p>Absolute value. Return an integer if <code>x</code> is an Integer. Otherwise -return a Float. No rounding is performed. -</p> -</dd> -<dt><code>min(a, b)</code></dt> -<dt><code>max(a, b)</code></dt> -<dd><p>No rounding is performed. The returned type is the same one as the -minimum (resp. maximum) value. -</p> -</dd> -</dl> - -<a name="Arbitrarily-large-floating-point-numbers"></a> -<h2 class="chapter">4 Arbitrarily large floating point numbers</h2> - -<a name="Introduction-3"></a> +<a name="Introduction-1"></a> <h3 class="section">4.1 Introduction</h3> <p>This extension adds the <code>BigFloat</code> primitive type. The @@ -628,8 +213,8 @@ environment are also set according to the result of the operation. point environment is used. </p> <p>The rounding mode of the global floating point environment is always -<code>RNDN</code> (“round to nearest with ties to even”)<a name="DOCF5" href="#FOOT5"><sup>5</sup></a>. The status flags of the global environment cannot be -read<a name="DOCF6" href="#FOOT6"><sup>6</sup></a>. The precision of the global environment is +<code>RNDN</code> (“round to nearest with ties to even”)<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>. The status flags of the global environment cannot be +read<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>. The precision of the global environment is <code>BigFloatEnv.prec</code>. The number of exponent bits of the global environment is <code>BigFloatEnv.expBits</code>. If <code>BigFloatEnv.expBits</code> is strictly smaller than the maximum allowed number of exponent bits @@ -646,7 +231,7 @@ when calling a function (see <code>BigFloatEnv.setPrec</code>). Hence a function can change the global floating point environment for its callees but not for its caller. </p> -<a name="Operators-1"></a> +<a name="Operators"></a> <h3 class="section">4.3 Operators</h3> <p>The builtin operators are extended so that a BigFloat is returned if @@ -669,9 +254,9 @@ equal when using the strict comparison operators (e.g. <code>0.0 === <p>BigFloat literals are floating point numbers with a trailing <code>l</code> suffix. BigFloat literals have an infinite precision. They are rounded according to the global floating point environment when they are -evaluated.<a name="DOCF7" href="#FOOT7"><sup>7</sup></a> +evaluated.<a name="DOCF3" href="#FOOT3"><sup>3</sup></a> </p> -<a name="Builtin-Object-changes-2"></a> +<a name="Builtin-Object-changes"></a> <h3 class="section">4.5 Builtin Object changes</h3> <a name="BigFloat-function"></a> @@ -739,6 +324,10 @@ point number <code>a</code> according to the floating point environment <dd><p>Round to an integer. No additional rounding is performed. </p> </dd> +<dt><code>abs(x)</code></dt> +<dd><p>Return the absolute value of x. No additional rounding is performed. +</p> +</dd> <dt><code>fmod(x, y[, e])</code></dt> <dt><code>remainder(x, y[, e])</code></dt> <dd><p>Floating point remainder. The quotient is truncated to zero (fmod) or @@ -773,6 +362,10 @@ number. <code>e</code> is an optional floating point environment. <p>The following properties are modified: </p> <dl compact="compact"> +<dt><code>valueOf()</code></dt> +<dd><p>Return the bigfloat primitive value corresponding to <code>this</code>. +</p> +</dd> <dt><code>toString(radix)</code></dt> <dd> <p>For floating point numbers: @@ -787,14 +380,17 @@ the global precision and round to nearest gives the same number. </li></ul> +<p>The exponent letter is <code>e</code> for base 10, <code>p</code> for bases 2, 8, +16 with a binary exponent and <code>@</code> for the other bases. +</p> </dd> -<dt><code>toPrecision(p[, rnd_mode])</code></dt> -<dt><code>toFixed(p[, rnd_mode])</code></dt> -<dt><code>toExponential(p[, rnd_mode])</code></dt> +<dt><code>toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> +<dt><code>toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> +<dt><code>toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> <dd><p>Same semantics as the corresponding <code>Number</code> functions with BigFloats. There is no limit on the accepted precision <code>p</code>. The -rounding mode can be optionally specified. It is set by default to -<code>BigFloatEnv.RNDNA</code>. +rounding mode and radix can be optionally specified. The radix must be +between 2 and 36. </p> </dd> </dl> @@ -832,14 +428,14 @@ subnormal flags is set to <code>false</code>. If <code>rndMode</code> is <dl compact="compact"> <dt><code>prec</code></dt> <dd><p>Getter. Return the precision in bits of the global floating point -environment. The initial value is <code>53</code>. +environment. The initial value is <code>113</code>. </p> </dd> <dt><code>expBits</code></dt> <dd><p>Getter. Return the exponent size in bits of the global floating point environment assuming an IEEE 754 representation. If <code>expBits < expBitsMax</code>, then subnormal numbers are supported. The initial value -is <code>11</code>. +is <code>15</code>. </p> </dd> <dt><code>setPrec(f, p[, e])</code></dt> @@ -848,9 +444,7 @@ and the exponent size to <code>e</code> then call the function <code>f</code>. Then the Float precision and exponent size are reset to their precious value and the return value of <code>f</code> is returned (or an exception is raised if <code>f</code> raised an exception). If <code>e</code> -is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>. <code>p</code> -must be >= 53 and <code>e</code> must be >= 11 so that the global precision -is at least equivalent to the IEEE 754 64 bit doubles. +is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>. </p> </dd> <dt><code>precMin</code></dt> @@ -858,7 +452,7 @@ is at least equivalent to the IEEE 754 64 bit doubles. </p> </dd> <dt><code>precMax</code></dt> -<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 53. +<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 113. </p> </dd> <dt><code>expBitsMin</code></dt> @@ -868,7 +462,7 @@ bits. Must be at least 3. </dd> <dt><code>expBitsMax</code></dt> <dd><p>Read-only integer. Return the maximum allowed exponent size in -bits. Must be at least 11. +bits. Must be at least 15. </p> </dd> <dt><code>RNDN</code></dt> @@ -891,11 +485,11 @@ bits. Must be at least 11. <dd><p>Read-only integer. Round to nearest, with ties away from zero rounding mode. </p> </dd> -<dt><code>RNDNU</code></dt> -<dd><p>Read-only integer. Round to nearest, with ties to +Infinity rounding mode. +<dt><code>RNDA</code></dt> +<dd><p>Read-only integer. Round away from zero rounding mode. </p> </dd> -<dt><code>RNDF<a name="DOCF8" href="#FOOT8"><sup>8</sup></a></code></dt> +<dt><code>RNDF<a name="DOCF4" href="#FOOT4"><sup>4</sup></a></code></dt> <dd><p>Read-only integer. Faithful rounding mode. The result is non-deterministically rounded to -Infinity or +Infinity. This rounding mode usually gives a faster and deterministic running time for the @@ -939,102 +533,200 @@ assuming an IEEE 754 representation. </dd> </dl> -<a name="Math-object-1"></a> -<h4 class="subsection">4.5.4 <code>Math</code> object</h4> +<a name="BigDecimal"></a> +<h2 class="chapter">5 BigDecimal</h2> -<p>The following properties are modified: +<p>This extension adds the <code>BigDecimal</code> primitive type. The +<code>BigDecimal</code> type represents floating point numbers in base +10. It is inspired from the proposal available at +<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>. +</p> +<p>The <code>BigDecimal</code> floating point numbers are always normalized and +finite. There is no concept of <code>-0</code>, <code>Infinity</code> or +<code>NaN</code>. By default, all the computations are done with infinite +precision. +</p> +<a name="Operators-1"></a> +<h3 class="section">5.1 Operators</h3> + +<p>The following builtin operators support BigDecimal: </p> <dl compact="compact"> -<dt><code>abs(x)</code></dt> -<dd><p>Absolute value. If <code>x</code> is a BigFloat, its absolute value is -returned as a BigFloat. No rounding is performed. +<dt><code>+</code></dt> +<dt><code>-</code></dt> +<dt><code>*</code></dt> +<dd><p>Both operands must be BigDecimal. The result is computed with infinite +precision. +</p></dd> +<dt><code>%</code></dt> +<dd><p>Both operands must be BigDecimal. The result is computed with infinite +precision. A range error is throws in case of division by zero. </p> </dd> -<dt><code>min(a, b)</code></dt> -<dt><code>max(a, b)</code></dt> -<dd><p>The returned type is the same one as the minimum (resp. maximum) -value, so <code>BigFloat</code> values are accepted. When a <code>BigFloat</code> -is returned, no rounding is performed. +<dt><code>/</code></dt> +<dd><p>Both operands must be BigDecimal. A range error is throws in case of +division by zero or if the result cannot be represented with infinite +precision (use <code>BigDecimal.div</code> to specify the rounding). +</p> +</dd> +<dt><code>**</code></dt> +<dd><p>Both operands must be BigDecimal. The exponent must be a positive +integer. The result is computed with infinite precision. +</p> +</dd> +<dt><code>===</code></dt> +<dd><p>When one of the operand is a BigDecimal, return true if both operands +are a BigDecimal and if they are equal. +</p> +</dd> +<dt><code>==</code></dt> +<dt><code>!=</code></dt> +<dt><code><=</code></dt> +<dt><code>>=</code></dt> +<dt><code><</code></dt> +<dt><code>></code></dt> +<dd> +<p>Numerical comparison. When one of the operand is not a BigDecimal, it is +converted to BigDecimal by using ToString(). Hence comparisons between +Number and BigDecimal do not use the exact mathematical value of the +Number value. </p> </dd> </dl> -<a name="Math-mode"></a> -<h2 class="chapter">5 Math mode</h2> +<a name="BigDecimal-literals"></a> +<h3 class="section">5.2 BigDecimal literals</h3> -<a name="Introduction-4"></a> -<h3 class="section">5.1 Introduction</h3> +<p>BigDecimal literals are decimal floating point numbers with a trailing +<code>m</code> suffix. +</p> +<a name="Builtin-Object-changes-1"></a> +<h3 class="section">5.3 Builtin Object changes</h3> -<p>A new <em>math mode</em> is enabled with the <code>"use math"</code> -directive. <code>"use bigint"</code> is implied in math mode. With this -mode, writing mathematical expressions is more intuitive, exact -results (e.g. fractions) can be computed for all operators and floating -point literals have the <code>BigFloat</code> type by default. +<a name="The-BigDecimal-function_002e"></a> +<h4 class="subsection">5.3.1 The <code>BigDecimal</code> function.</h4> + +<p>It returns <code>0m</code> if no parameter is provided. Otherwise the first +parameter is converted to a bigdecimal by using ToString(). Hence +Number value are not converted to their exact numerical value as +BigDecimal. </p> -<p>It propagates the same way as the <em>strict mode</em>. In -this mode: +<a name="Properties-of-the-BigDecimal-object"></a> +<h4 class="subsection">5.3.2 Properties of the <code>BigDecimal</code> object</h4> + +<dl compact="compact"> +<dt><code>add(a, b[, e])</code></dt> +<dt><code>sub(a, b[, e])</code></dt> +<dt><code>mul(a, b[, e])</code></dt> +<dt><code>div(a, b[, e])</code></dt> +<dt><code>mod(a, b[, e])</code></dt> +<dt><code>sqrt(a, e)</code></dt> +<dt><code>round(a, e)</code></dt> +<dd><p>Perform the specified floating point operation and round the floating +point result according to the rounding object <code>e</code>. If the +rounding object is not present, the operation is executed with +infinite precision. </p> -<ul> -<li> The <code>^</code> operator is a similar to the power operator (<code>**</code>). +<p>For <code>div</code>, a <code>RangeError</code> exception is thrown in case of +division by zero or if the result cannot be represented with infinite +precision if no rounding object is present. +</p> +<p>For <code>sqrt</code>, a range error is thrown if <code>a</code> is less than +zero. +</p> +<p>The rounding object must contain the following properties: +<code>roundingMode</code> is a string specifying the rounding mode +(<code>"floor"</code>, <code>"ceiling"</code>, <code>"down"</code>, <code>"up"</code>, +<code>"half-even"</code>, <code>"half-up"</code>). Either +<code>maximumSignificantDigits</code> or <code>maximumFractionDigits</code> must +be present to specify respectively the number of significant digits +(must be >= 1) or the number of digits after the decimal point (must +be >= 0). +</p> +</dd> +</dl> -</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>. +<a name="Properties-of-the-BigDecimal_002eprototype-object"></a> +<h4 class="subsection">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</h4> -</li><li> The logical xor operator is still available with the <code>^^</code> operator. +<dl compact="compact"> +<dt><code>valueOf()</code></dt> +<dd><p>Return the bigdecimal primitive value corresponding to <code>this</code>. +</p> +</dd> +<dt><code>toString()</code></dt> +<dd><p>Convert <code>this</code> to a string with infinite precision in base 10. +</p> +</dd> +<dt><code>toPrecision(p, rnd_mode = "half-up")</code></dt> +<dt><code>toFixed(p, rnd_mode = "half-up")</code></dt> +<dt><code>toExponential(p, rnd_mode = "half-up")</code></dt> +<dd><p>Convert the BigDecimal <code>this</code> to string with the specified +precision <code>p</code>. There is no limit on the accepted precision +<code>p</code>. The rounding mode can be optionally +specified. <code>toPrecision</code> outputs either in decimal fixed notation +or in decimal exponential notation with a <code>p</code> digits of +precision. <code>toExponential</code> outputs in decimal exponential +notation with <code>p</code> digits after the decimal point. <code>toFixed</code> +outputs in decimal notation with <code>p</code> digits after the decimal +point. +</p> +</dd> +</dl> -</li><li> The division operator invokes <code>BigInt[Symbol.operatorDiv]</code> in case both operands are integers. +<a name="Math-mode"></a> +<h2 class="chapter">6 Math mode</h2> -</li><li> The power operator invokes <code>BigInt[Symbol.operatorPow]</code> in case both operands are integers and the exponent is strictly negative. +<p>A new <em>math mode</em> is enabled with the <code>"use math"</code> +directive. It propagates the same way as the <em>strict mode</em>. It is +designed so that arbitrarily large integers and floating point numbers +are available by default. In order to minimize the number of changes +in the Javascript semantics, integers are represented either as Number +or BigInt depending on their magnitude. Floating point numbers are +always represented as BigFloat. +</p> +<p>The following changes are made to the Javascript semantics: +</p> +<ul> +<li> Floating point literals (i.e. number with a decimal point or an exponent) are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied). Hence <code>typeof 1.0 === "bigfloat"</code>. -</li><li> The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder. +</li><li> Integer literals (i.e. numbers without a decimal point or an exponent) with or without the <code>n</code> suffix are <code>BigInt</code> if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to <code>2**53-1</code>. Hence <code>typeof 1 === "number "</code>, <code>typeof 1n === "number"</code> but <code>typeof 9007199254740992 === "bigint" </code>. -</li><li> Floating point literals are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied). +</li><li> All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt. -</li></ul> +</li><li> The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result. -<a name="Builtin-Object-changes-3"></a> -<h3 class="section">5.2 Builtin Object changes</h3> +</li><li> The <code>^</code> operator is an alias to the power operator (<code>**</code>). -<a name="Symbol-constructor-1"></a> -<h4 class="subsection">5.2.1 <code>Symbol</code> constructor</h4> +</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>. -<p>The following global symbol is added for the operator overloading: -</p><dl compact="compact"> -<dt><code>operatorMathMod</code></dt> -</dl> +</li><li> The logical xor operator is still available with the <code>^^</code> operator. -<a name="Remaining-issues"></a> -<h3 class="section">5.3 Remaining issues</h3> +</li><li> The integer division operator can be overloaded by modifying the corresponding operator in <code>BigInt.prototype.[[OperatorSet]]</code>. -<ol> -<li> A new floating point literal suffix could be added for <code>Number</code> literals. +</li><li> The integer power operator with a non zero negative exponent can be overloaded by modifying the corresponding operator in <code>BigInt.prototype.[[OperatorSet]]</code>. -</li></ol> +</li><li> The modulo operator (<code>%</code>) returns the Euclidian remainder (always positive) instead of the truncated remainder. + +</li></ul> <div class="footnote"> <hr> <h4 class="footnotes-heading">Footnotes</h4> <h3><a name="FOOT1" href="#DOCF1">(1)</a></h3> -<p><a href="https://tc39.github.io/proposal-bigint/">https://tc39.github.io/proposal-bigint/</a></p> -<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3> -<p><a href="https://tc39.github.io/proposal-bigint/">https://tc39.github.io/proposal-bigint/</a></p> -<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3> -<p>Could be extended to 53 bits without changing the principle.</p> -<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3> -<p>The unsigned right right operator could be removed in bigint mode.</p> -<h3><a name="FOOT5" href="#DOCF5">(5)</a></h3> <p>The rationale is that the rounding mode changes must always be explicit.</p> -<h3><a name="FOOT6" href="#DOCF6">(6)</a></h3> +<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3> <p>The rationale is to avoid side effects for the built-in operators.</p> -<h3><a name="FOOT7" href="#DOCF7">(7)</a></h3> +<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3> <p>Base 10 floating point literals cannot usually be exactly represented as base 2 floating point number. In order to ensure that the literal is represented accurately with the current precision, it must be evaluated at runtime.</p> -<h3><a name="FOOT8" href="#DOCF8">(8)</a></h3> +<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3> <p>Could be removed in case a deterministic behavior for floating point operations is required.</p> </div> <hr> diff --git a/doc/jsbignum.pdf b/doc/jsbignum.pdf Binary files differindex 16a0a35..09d4142 100644 --- a/doc/jsbignum.pdf +++ b/doc/jsbignum.pdf diff --git a/doc/jsbignum.texi b/doc/jsbignum.texi index 8b69eb2..70455ec 100644 --- a/doc/jsbignum.texi +++ b/doc/jsbignum.texi @@ -10,12 +10,12 @@ @sp 7 @center @titlefont{Javascript Bignum Extensions} @sp 3 -@center Version 2018-06-16 +@center Version 2020-01-11 @sp 3 @center Author: Fabrice Bellard @end titlepage -@setfilename spec.info +@setfilename jsbignum.info @settitle Javascript Bignum Extensions @contents @@ -27,348 +27,52 @@ language while being 100% backward compatible: @itemize -@item Overloading of the standard operators -to support new types such as complex numbers, fractions or matrices. - -@item Bigint mode where arbitrarily large integers are available by default (no @code{n} suffix is necessary as in the TC39 BigInt proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}). +@item Operator overloading with a dispatch logic inspired from the proposal available at @url{https://github.com/tc39/proposal-operator-overloading/}. @item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics. -@item Optional @code{math} mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder. +@item Arbitrarily large floating point numbers (@code{BigDecimal}) in base 10 based on the proposal available at +@url{https://github.com/littledan/proposal-bigdecimal}. + +@item @code{math} mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (@code{%}) is defined as the Euclidian +remainder. @code{^} is an alias to the power operator +(@code{**}). @code{^^} is used as the exclusive or operator. @end itemize The extensions are independent from each other except the @code{math} -mode which relies on the bigint mode and the operator overloading. +mode which relies on BigFloat and operator overloading. @chapter Operator overloading -@section Introduction - -If the operands of an operator have at least one object type, a custom -operator method is searched before doing the legacy Javascript -@code{ToNumber} conversion. - -For unary operators, the custom function is looked up in the object -and has the following name: - -@table @code -@item unary + -@code{Symbol.operatorPlus} - -@item unary - -@code{Symbol.operatorNeg} - -@item ++ -@code{Symbol.operatorInc} - -@item -- -@code{Symbol.operatorDec} - -@item ~ -@code{Symbol.operatorNot} - -@end table - -For binary operators: - -@itemize - -@item -If both operands have the same constructor function, then the operator -is looked up in the constructor. - -@item -Otherwise, the property @code{Symbol.operatorOrder} is looked up in both -constructors and converted to @code{Int32}. The operator is then -looked in the constructor with the larger @code{Symbol.operatorOrder} -value. A @code{TypeError} is raised if both constructors have the same -@code{Symbol.operatorOrder} value. - -@end itemize - -The operator is looked up with the following name: - -@table @code -@item + -@code{Symbol.operatorAdd} - -@item - -@code{Symbol.operatorSub} - -@item * -@code{Symbol.operatorMul} - -@item / -@code{Symbol.operatorDiv} - -@item % -@code{Symbol.operatorMod} - -@item % (math mode) -@code{Symbol.operatorMathMod} - -@item ** -@code{Symbol.operatorPow} - -@item | -@code{Symbol.operatorOr} - -@item ^ -@code{Symbol.operatorXor} - -@item & -@code{Symbol.operatorAnd} - -@item << -@code{Symbol.operatorShl} - -@item >> -@code{Symbol.operatorShr} - -@item < -@code{Symbol.operatorCmpLT} - -@item > -@code{Symbol.operatorCmpLT}, operands swapped - -@item <= -@code{Symbol.operatorCmpLE} - -@item >= -@code{Symbol.operatorCmpLE}, operands swapped - -@item ==, != -@code{Symbol.operatorCmpEQ} - -@end table - -The return value of @code{Symbol.operatorCmpLT}, @code{Symbol.operatorCmpLE} and -@code{Symbol.operatorCmpEQ} is converted to @code{Boolean}. - -@section Builtin Object changes - -@subsection @code{Symbol} constructor - -The following global symbols are added for the operator overloading: -@table @code -@item operatorOrder -@item operatorAdd -@item operatorSub -@item operatorMul -@item operatorDiv -@item operatorMod -@item operatorPow -@item operatorShl -@item operatorShr -@item operatorAnd -@item operatorOr -@item operatorXor -@item operatorCmpLT -@item operatorCmpLE -@item operatorCmpEQ -@item operatorPlus -@item operatorNeg -@item operatorNot -@item operatorInc -@item operatorDec -@end table - - -@chapter The BigInt Mode +Operator overloading is inspired from the proposal available at +@url{https://github.com/tc39/proposal-operator-overloading/}. It +implements the same dispatch logic but finds the operator sets by +looking at the @code{Symbol.operatorSet} property in the objects. The +changes were done in order to simplify the implementation. -@section Introduction - -The bigint mode is enabled with the @code{"use bigint"} directive. It -propagates the same way as the strict mode. In bigint mode, all -integers are considered as @code{bigint} (arbitrarily large integer, -similar to the TC39 BigInt -proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}) -instead of @code{number} (floating point number). In order to be able -to exchange data between standard and bigint modes, numbers are -internally represented as 3 different types: +More precisely, the following modifications were made: @itemize -@item Small integer (SmallInt): 32 bit integer@footnote{Could be extended to 53 bits without changing the principle.}. - -@item Big integer (BigInt): arbitrarily large integer. - -@item Floating point number (Float). - -@end itemize - -In standard mode, the semantics of each operation is modified so that -when it returns a @code{number}, it is either of SmallInt or -Float. But the difference between SmallInt and Float is not observable -in standard mode. - -In bigint mode, each operation behaves differently whether its -operands are integer or float. The difference between SmallInt and -BigInt is not observable (i.e. they are both integers). - -The following table summarizes the observable types: - -@multitable @columnfractions .3 .3 .3 -@headitem Internal type @tab Observable type@* (standard mode) @tab Observable type@* (bigint mode) -@item SmallInt @tab number @tab bigint -@item BigInt @tab bigint @tab bigint -@item Float @tab number @tab number -@end multitable - -@section Changes that introduce incompatibilities with Javascript - -@subsection Standard mode - -There is no incompatibility with Javascript. - -@subsection Bigint mode - -The following changes are visible: - -@itemize - -@item Integer and Float are different types. Constants are typed. For example: @code{typeof 1.0 === "number"} and @code{typeof 1 === "bigint"}. Another consequence is that @code{1.0 === 1} is false. - -@item The range of integers is unlimited. In standard mode: @code{2**53 + 1 === 2**53}. This is no longer true with the bignum extensions. - -@item Binary bitwise operators do not truncate to 32 bits i.e. @code{0x800000000 | 1 === 0x800000001} while it gives @code{1} in standard mode. +@item @code{with operators from} is not supported. Operator overloading is always enabled. -@item Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with @code{0x1f} i.e. @code{1 << 32 === 4294967296} while it gives @code{1} in standard mode. However, the @code{>>>} operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior@footnote{The unsigned right right operator could be removed in bigint mode.}. +@item The dispatch is not based on a static @code{[[OperatorSet]]} field in all instances. Instead, a dynamic lookup the of the @code{Symbol.operatorSet} property is done. This property is typically added in the prototype of each object. -@item Operators with integer operands never return the minus zero floating point value as result. Hence @code{Object.is(0, -0) === true}. Use @code{-0.0} to create a minus zero floating point value. +@item @code{Operators.create(...dictionaries)} is used to create a new OperatorSet object. The @code{Operators} function is supported as an helper to be closer to the TC39 proposal. -@item The @code{ToPrimitive} abstract operation is called with the @code{"integer"} preferred type when an integer is required (e.g. for bitwise binary or shift operations). +@item @code{[]} cannot be overloaded. -@item The prototype of integers is no longer @code{Number.prototype}. Instead@* @code{Object.getPrototypeOf(1) === BigInt.prototype}. The prototype of floats remains Number.prototype. +@item In math mode, the BigInt division and power operators can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. -@item If the TC39 BigInt proposal is supported, there is no observable difference between integers and @code{bigint}s. - -@end itemize - -@section Operators - -@subsection Arithmetic operators - -The operands are converted to number values as in normal -Javascript. Then the general case is that an Integer is returned if -both operands are Integer. Otherwise, a float is returned. - -The @code{+} operator also accepts strings as input and behaves like -standard Javascript in this case. - -The binary operator @code{%} returns the truncated remainder of the -division. When the result is an Integer type, a dividend of zero yields a -RangeError exception. - -The binary operator @code{%} in math mode returns the Euclidian -remainder of the division i.e. it is always positive. - -The binary operator @code{/} returns a float. - -The binary operator @code{/} in math mode returns a float if one of -the operands is float. Otherwise, @code{BigInt[Symbol.operatorDiv]} is -invoked. - -The returned type of @code{a ** b} is Float if @math{a} or @math{b} -are Float. If @math{a} and @math{b} are integers: -@itemize -@item @math{b < 0} returns a Float in bigint mode. In math mode, @code{BigInt[Symbol.operatorPow]} is invoked. - -@item @math{b >= 0} returns an integer. @end itemize -The unary @code{-} and unary @code{+} return the same type as their -operand. They performs no floating point rounding when the result is a -float. - -The unary operators @code{++} and @code{--} return the same type as -their operand. - -In standard mode: - -If the operator returns an Integer and that the result fits a -SmallInt, it is converted to SmallInt. Otherwise, the Integer is -converted to a Float. - -In bigint mode: - -If the operator returns an Integer and that the result fits a -SmallInt, it is converted to SmallInt. Otherwise it is a BigInt. - -@subsection Logical operators - -In standard mode: - -The operands have their standard behavior. If the result fits a -SmallInt it is converted to a SmallInt. Otherwise it is a Float. - -In bigint mode: - -The operands are converted to integer values. The floating point -values are converted to integer by rounding them to zero. - -The logical operators are defined assuming the integers are -represented in two complement notation. - -For @code{<<} and @code{<<}, the shift can be positive or negative. So -@code{a << b} is defined as @math{\lfloor a/2^{-b} \rfloor} and -@code{a >> b} is defined as @math{\lfloor a/2^{b} \rfloor}. - -The operator @code{>>>} is supported for backward compatibility and -behaves the same way as Javascript i.e. implicit conversion to @code{Uint32}. +@chapter BigInt extensions -If the result fits a SmallInt it is converted to a SmallInt. Otherwise -it is a BigInt. - -@subsection Relational operators - -The relational operators <, <=, >, >=, ==, != work as expected with -integers and floating point numbers (e.g. @code{1.0 == 1} is true). - -The strict equality operators === and !== have the usual Javascript -semantics. In particular, different types never equal, so @code{1.0 -=== 1} is false. - -@section Number literals - -Number literals in bigint mode have a slightly different behavior than -in standard Javascript: - -@enumerate - -@item -A number literal without a decimal point or an exponent is considered -as an Integer. Otherwise it is a Float. - -@item -Hexadecimal, octal or binary floating point literals are accepted with -a decimal point or an exponent. The exponent is specified with the -@code{p} letter assuming a base 2. The same convention is used by -C99. Example: @code{0x1p3} is the same as @code{8.0}. - -@end enumerate - -@section Builtin Object changes - -@subsection @code{BigInt} function - -The @code{BigInt} function cannot be invoked as a constructor. When -invoked as a function, it converts its first parameter to an -integer. When a floating point number is given as parameter, it is -truncated to an integer with infinite precision. - -@code{BigInt} properties: +A few properties are added to the BigInt object: @table @code -@item asIntN(bits, a) -Set @math{b=a \pmod{2^{bits}}}. Return @math{b} if @math{b < 2^{bits-1}} -otherwise @math{b-2^{bits}}. - -@item asUintN(bits, a) -Return @math{a \pmod{2^{bits}}}. - @item tdiv(a, b) Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError exception. @@ -410,58 +114,7 @@ Return the number of trailing zeros in the two's complement binary representatio @end table -@subsection @code{BigInt.prototype} - -It is a normal object. - -@subsection @code{Number} constructor - -The number constructor returns its argument rounded to a Float using -the global floating point environment. In bigint mode, the Number -constructor returns a Float. In standard mode, it returns a SmallInt -if the value fits it, otherwise a Float. - -@subsection @code{Number.prototype} - -The following properties are modified: - -@table @code -@item toString(radix) - -In bigint mode, integers are converted to the specified radix with -infinite precision. - -@item toPrecision(p) -@item toFixed(p) -@item toExponential(p) - -In bigint mode, integers are accepted and converted to string with -infinite precision. - -@item parseInt(string, radix) - -In bigint mode, an integer is returned and the conversion is done with -infinite precision. - -@end table - -@subsection @code{Math} object - -The following properties are modified: - -@table @code -@item abs(x) -Absolute value. Return an integer if @code{x} is an Integer. Otherwise -return a Float. No rounding is performed. - -@item min(a, b) -@item max(a, b) -No rounding is performed. The returned type is the same one as the -minimum (resp. maximum) value. - -@end table - -@chapter Arbitrarily large floating point numbers +@chapter BigFloat @section Introduction @@ -590,6 +243,9 @@ point number @code{a} according to the floating point environment @item trunc(x) Round to an integer. No additional rounding is performed. +@item abs(x) +Return the absolute value of x. No additional rounding is performed. + @item fmod(x, y[, e]) @item remainder(x, y[, e]) Floating point remainder. The quotient is truncated to zero (fmod) or @@ -620,6 +276,9 @@ number. @code{e} is an optional floating point environment. The following properties are modified: @table @code +@item valueOf() +Return the bigfloat primitive value corresponding to @code{this}. + @item toString(radix) For floating point numbers: @@ -636,13 +295,16 @@ the global precision and round to nearest gives the same number. @end itemize -@item toPrecision(p[, rnd_mode]) -@item toFixed(p[, rnd_mode]) -@item toExponential(p[, rnd_mode]) +The exponent letter is @code{e} for base 10, @code{p} for bases 2, 8, +16 with a binary exponent and @code{@@} for the other bases. + +@item toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) +@item toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) +@item toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) Same semantics as the corresponding @code{Number} functions with BigFloats. There is no limit on the accepted precision @code{p}. The -rounding mode can be optionally specified. It is set by default to -@code{BigFloatEnv.RNDNA}. +rounding mode and radix can be optionally specified. The radix must be +between 2 and 36. @end table @@ -679,13 +341,13 @@ subnormal flags is set to @code{false}. If @code{rndMode} is @item prec Getter. Return the precision in bits of the global floating point -environment. The initial value is @code{53}. +environment. The initial value is @code{113}. @item expBits Getter. Return the exponent size in bits of the global floating point environment assuming an IEEE 754 representation. If @code{expBits < expBitsMax}, then subnormal numbers are supported. The initial value -is @code{11}. +is @code{15}. @item setPrec(f, p[, e]) Set the precision of the global floating point environment to @code{p} @@ -693,15 +355,13 @@ and the exponent size to @code{e} then call the function @code{f}. Then the Float precision and exponent size are reset to their precious value and the return value of @code{f} is returned (or an exception is raised if @code{f} raised an exception). If @code{e} -is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @code{p} -must be >= 53 and @code{e} must be >= 11 so that the global precision -is at least equivalent to the IEEE 754 64 bit doubles. +is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @item precMin Read-only integer. Return the minimum allowed precision. Must be at least 2. @item precMax -Read-only integer. Return the maximum allowed precision. Must be at least 53. +Read-only integer. Return the maximum allowed precision. Must be at least 113. @item expBitsMin Read-only integer. Return the minimum allowed exponent size in @@ -709,7 +369,7 @@ bits. Must be at least 3. @item expBitsMax Read-only integer. Return the maximum allowed exponent size in -bits. Must be at least 11. +bits. Must be at least 15. @item RNDN Read-only integer. Round to nearest, with ties to even rounding mode. @@ -726,8 +386,8 @@ Read-only integer. Round to +Infinity rounding mode. @item RNDNA Read-only integer. Round to nearest, with ties away from zero rounding mode. -@item RNDNU -Read-only integer. Round to nearest, with ties to +Infinity rounding mode. +@item RNDA +Read-only integer. Round away from zero rounding mode. @item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.} Read-only integer. Faithful rounding mode. The result is @@ -767,69 +427,166 @@ Getter and setter (Boolean). Status flags. @end table -@subsection @code{Math} object +@chapter BigDecimal -The following properties are modified: +This extension adds the @code{BigDecimal} primitive type. The +@code{BigDecimal} type represents floating point numbers in base +10. It is inspired from the proposal available at +@url{https://github.com/littledan/proposal-bigdecimal}. + +The @code{BigDecimal} floating point numbers are always normalized and +finite. There is no concept of @code{-0}, @code{Infinity} or +@code{NaN}. By default, all the computations are done with infinite +precision. + +@section Operators + +The following builtin operators support BigDecimal: @table @code -@item abs(x) -Absolute value. If @code{x} is a BigFloat, its absolute value is -returned as a BigFloat. No rounding is performed. -@item min(a, b) -@item max(a, b) -The returned type is the same one as the minimum (resp. maximum) -value, so @code{BigFloat} values are accepted. When a @code{BigFloat} -is returned, no rounding is performed. +@item + +@item - +@item * +Both operands must be BigDecimal. The result is computed with infinite +precision. +@item % +Both operands must be BigDecimal. The result is computed with infinite +precision. A range error is throws in case of division by zero. -@end table +@item / +Both operands must be BigDecimal. A range error is throws in case of +division by zero or if the result cannot be represented with infinite +precision (use @code{BigDecimal.div} to specify the rounding). -@chapter Math mode +@item ** +Both operands must be BigDecimal. The exponent must be a positive +integer. The result is computed with infinite precision. -@section Introduction +@item === +When one of the operand is a BigDecimal, return true if both operands +are a BigDecimal and if they are equal. -A new @emph{math mode} is enabled with the @code{"use math"} -directive. @code{"use bigint"} is implied in math mode. With this -mode, writing mathematical expressions is more intuitive, exact -results (e.g. fractions) can be computed for all operators and floating -point literals have the @code{BigFloat} type by default. +@item == +@item != +@item <= +@item >= +@item < +@item > -It propagates the same way as the @emph{strict mode}. In -this mode: +Numerical comparison. When one of the operand is not a BigDecimal, it is +converted to BigDecimal by using ToString(). Hence comparisons between +Number and BigDecimal do not use the exact mathematical value of the +Number value. -@itemize +@end table -@item The @code{^} operator is a similar to the power operator (@code{**}). +@section BigDecimal literals -@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}. +BigDecimal literals are decimal floating point numbers with a trailing +@code{m} suffix. -@item The logical xor operator is still available with the @code{^^} operator. +@section Builtin Object changes -@item The division operator invokes @code{BigInt[Symbol.operatorDiv]} in case both operands are integers. +@subsection The @code{BigDecimal} function. -@item The power operator invokes @code{BigInt[Symbol.operatorPow]} in case both operands are integers and the exponent is strictly negative. +It returns @code{0m} if no parameter is provided. Otherwise the first +parameter is converted to a bigdecimal by using ToString(). Hence +Number value are not converted to their exact numerical value as +BigDecimal. -@item The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder. +@subsection Properties of the @code{BigDecimal} object -@item Floating point literals are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). +@table @code -@end itemize +@item add(a, b[, e]) +@item sub(a, b[, e]) +@item mul(a, b[, e]) +@item div(a, b[, e]) +@item mod(a, b[, e]) +@item sqrt(a, e) +@item round(a, e) +Perform the specified floating point operation and round the floating +point result according to the rounding object @code{e}. If the +rounding object is not present, the operation is executed with +infinite precision. -@section Builtin Object changes +For @code{div}, a @code{RangeError} exception is thrown in case of +division by zero or if the result cannot be represented with infinite +precision if no rounding object is present. + +For @code{sqrt}, a range error is thrown if @code{a} is less than +zero. + +The rounding object must contain the following properties: +@code{roundingMode} is a string specifying the rounding mode +(@code{"floor"}, @code{"ceiling"}, @code{"down"}, @code{"up"}, +@code{"half-even"}, @code{"half-up"}). Either +@code{maximumSignificantDigits} or @code{maximumFractionDigits} must +be present to specify respectively the number of significant digits +(must be >= 1) or the number of digits after the decimal point (must +be >= 0). -@subsection @code{Symbol} constructor +@end table + +@subsection Properties of the @code{BigDecimal.prototype} object -The following global symbol is added for the operator overloading: @table @code -@item operatorMathMod +@item valueOf() +Return the bigdecimal primitive value corresponding to @code{this}. + +@item toString() +Convert @code{this} to a string with infinite precision in base 10. + +@item toPrecision(p, rnd_mode = "half-up") +@item toFixed(p, rnd_mode = "half-up") +@item toExponential(p, rnd_mode = "half-up") +Convert the BigDecimal @code{this} to string with the specified +precision @code{p}. There is no limit on the accepted precision +@code{p}. The rounding mode can be optionally +specified. @code{toPrecision} outputs either in decimal fixed notation +or in decimal exponential notation with a @code{p} digits of +precision. @code{toExponential} outputs in decimal exponential +notation with @code{p} digits after the decimal point. @code{toFixed} +outputs in decimal notation with @code{p} digits after the decimal +point. + @end table -@section Remaining issues +@chapter Math mode -@enumerate +A new @emph{math mode} is enabled with the @code{"use math"} +directive. It propagates the same way as the @emph{strict mode}. It is +designed so that arbitrarily large integers and floating point numbers +are available by default. In order to minimize the number of changes +in the Javascript semantics, integers are represented either as Number +or BigInt depending on their magnitude. Floating point numbers are +always represented as BigFloat. + +The following changes are made to the Javascript semantics: -@item A new floating point literal suffix could be added for @code{Number} literals. +@itemize -@end enumerate +@item Floating point literals (i.e. number with a decimal point or an exponent) are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). Hence @code{typeof 1.0 === "bigfloat"}. + +@item Integer literals (i.e. numbers without a decimal point or an exponent) with or without the @code{n} suffix are @code{BigInt} if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to @code{2**53-1}. Hence @code{typeof 1 === "number "}, @code{typeof 1n === "number"} but @code{typeof 9007199254740992 === "bigint" }. + +@item All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt. + +@item The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result. + +@item The @code{^} operator is an alias to the power operator (@code{**}). + +@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}. + +@item The logical xor operator is still available with the @code{^^} operator. + +@item The integer division operator can be overloaded by modifying the corresponding operator in @code{BigInt.prototype.[[OperatorSet]]}. + +@item The integer power operator with a non zero negative exponent can be overloaded by modifying the corresponding operator in @code{BigInt.prototype.[[OperatorSet]]}. + +@item The modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder. + +@end itemize @bye diff --git a/doc/quickjs.html b/doc/quickjs.html index f3522d7..11657a3 100644 --- a/doc/quickjs.html +++ b/doc/quickjs.html @@ -1,7 +1,8 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> -<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ --> +<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ --> <head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>QuickJS Javascript Engine</title> <meta name="description" content="QuickJS Javascript Engine"> @@ -9,7 +10,6 @@ <meta name="resource-type" content="document"> <meta name="distribution" content="global"> <meta name="Generator" content="makeinfo"> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link href="#SEC_Contents" rel="contents" title="Table of Contents"> <style type="text/css"> <!-- @@ -206,7 +206,7 @@ Javascript files and/or expressions as arguments to execute them: <a name="qjs-interpreter"></a> <h4 class="subsection">2.3.1 <code>qjs</code> interpreter</h4> -<pre class="verbatim">usage: qjs [options] [files] +<pre class="verbatim">usage: qjs [options] [file [args]] </pre> <p>Options are: </p><dl compact="compact"> @@ -241,6 +241,11 @@ source is <code>import</code>. the <code>"use bigint"</code> and <code>"use math"</code> directives. </p> </dd> +<dt><code>-I file</code></dt> +<dt><code>--include file</code></dt> +<dd><p>Include an additional file. +</p> +</dd> </dl> <p>Advanced options are: @@ -767,6 +772,10 @@ pathname of <code>path</code> and <code>err</code> the error code. and <code>err</code> the error code. </p> </dd> +<dt><code>chdir(path)</code></dt> +<dd><p>Change the current directory. Return the error code. +</p> +</dd> <dt><code>mkdir(path, mode = 0o777)</code></dt> <dd><p>Create a directory at <code>path</code>. Return the error code. </p> diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf Binary files differindex 68352cf..99bebfc 100644 --- a/doc/quickjs.pdf +++ b/doc/quickjs.pdf diff --git a/doc/quickjs.texi b/doc/quickjs.texi index 696a52d..aba1ab5 100644 --- a/doc/quickjs.texi +++ b/doc/quickjs.texi @@ -92,7 +92,7 @@ generates a @code{hello} executable with no external dependency. @subsection @code{qjs} interpreter @verbatim -usage: qjs [options] [files] +usage: qjs [options] [file [args]] @end verbatim Options are: @@ -122,6 +122,10 @@ Load as ES6 script (default=autodetect). Enable the bignum extensions: BigDecimal object, BigFloat object and the @code{"use bigint"} and @code{"use math"} directives. +@item -I file +@item --include file +Include an additional file. + @end table Advanced options are: @@ -576,6 +580,9 @@ pathname of @code{path} and @code{err} the error code. Return @code{[str, err]} where @code{str} is the current working directory and @code{err} the error code. +@item chdir(path) +Change the current directory. Return the error code. + @item mkdir(path, mode = 0o777) Create a directory at @code{path}. Return the error code. diff --git a/examples/pi_bigdecimal.js b/examples/pi_bigdecimal.js index 7d5eccf..6a416b7 100644 --- a/examples/pi_bigdecimal.js +++ b/examples/pi_bigdecimal.js @@ -6,10 +6,10 @@ /* compute PI with a precision of 'prec' digits */ function calc_pi(prec) { - const CHUD_A = 13591409d; - const CHUD_B = 545140134d; - const CHUD_C = 640320d; - const CHUD_C3 = 10939058860032000d; /* C^3/24 */ + const CHUD_A = 13591409m; + const CHUD_B = 545140134m; + const CHUD_C = 640320m; + const CHUD_C3 = 10939058860032000m; /* C^3/24 */ const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */ /* return [P, Q, G] */ @@ -17,7 +17,7 @@ function calc_pi(prec) { var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1; if (a == (b - 1n)) { b1 = BigDecimal(b); - G = (2d * b1 - 1d) * (6d * b1 - 1d) * (6d * b1 - 5d); + G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m); P = G * (CHUD_B * b1 + CHUD_A); if (b & 1n) P = -P; @@ -32,7 +32,7 @@ function calc_pi(prec) { if (need_G) G = G1 * G2; else - G = 0d; + G = 0m; } return [P, Q, G]; } @@ -44,7 +44,7 @@ function calc_pi(prec) { Q = BigDecimal.div(Q, (P + Q * CHUD_A), { roundingMode: "half-even", maximumSignificantDigits: prec }); - G = (CHUD_C / 12d) * BigDecimal.sqrt(CHUD_C, + G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C, { roundingMode: "half-even", maximumSignificantDigits: prec }); return Q * G; @@ -64,7 +64,5 @@ function calc_pi(prec) { /* we add more digits to reduce the probability of bad rounding for the last digits */ r = calc_pi(n_digits + 20); - r = BigDecimal.round(r, { roundingMode: "down", - maximumFractionDigits: n_digits }) - print(r); + print(r.toFixed(n_digits, "down")); })(); @@ -86,7 +86,6 @@ static no_inline int fft_mul(bf_context_t *s, static void fft_clear_cache(bf_context_t *s); #endif #ifdef USE_BF_DEC -static void mp_pow_init(void); static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); #endif @@ -154,6 +153,17 @@ static inline limb_t smod(slimb_t a, slimb_t b) return a; } +/* signed addition with saturation */ +static inline slimb_t sat_add(slimb_t a, slimb_t b) +{ + slimb_t r; + r = a + b; + /* overflow ? */ + if (((a ^ r) & (b ^ r)) < 0) + r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); + return r; +} + #define malloc(s) malloc_is_forbidden(s) #define free(p) free_is_forbidden(p) #define realloc(p, s) realloc_is_forbidden(p, s) @@ -164,9 +174,6 @@ void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, memset(s, 0, sizeof(*s)); s->realloc_func = realloc_func; s->realloc_opaque = realloc_opaque; -#ifdef USE_BF_DEC - mp_pow_init(); -#endif } void bf_context_end(bf_context_t *s) @@ -422,18 +429,13 @@ static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, if (r->sign == (rnd_mode == BF_RNDD)) add_one = inexact; break; + case BF_RNDA: + add_one = inexact; + break; case BF_RNDNA: case BF_RNDF: add_one = bit1; break; - case BF_RNDNU: - if (bit1) { - if (r->sign) - add_one = bit0; - else - add_one = 1; - } - break; default: abort(); } @@ -452,7 +454,7 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) if (prec == BF_PREC_INF || rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU || + rnd_mode == BF_RNDA || (rnd_mode == BF_RNDD && sign == 1) || (rnd_mode == BF_RNDU && sign == 0)) { bf_set_inf(r, sign); @@ -476,12 +478,14 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) /* round to prec1 bits assuming 'r' is non zero and finite. 'r' is assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be - infinite (BF_PREC_INF). Can fail with BF_ST_MEM_ERROR in case of + infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result + is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of overflow not returning infinity. */ -static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l) +static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, + int ret) { limb_t v, a; - int shift, add_one, ret, rnd_mode; + int shift, add_one, rnd_mode; slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; /* e_min and e_max are computed to match the IEEE 754 conventions */ @@ -506,7 +510,6 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l) /* round to prec bits */ rnd_mode = flags & BF_RND_MASK; - ret = 0; add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); if (prec <= 0) { @@ -615,7 +618,7 @@ int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) } r->expn -= shift; } - ret = __bf_round(r, prec1, flags, l); + ret = __bf_round(r, prec1, flags, l, 0); } // bf_print_str("r_final", r); return ret; @@ -638,8 +641,7 @@ int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) } if (a->expn == BF_EXP_ZERO) return FALSE; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU); + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); if (k < (prec + 2)) return FALSE; bit_pos = a->len * LIMB_BITS - 1 - prec; @@ -666,7 +668,7 @@ int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) { if (r->len == 0) return 0; - return __bf_round(r, prec, flags, r->len); + return __bf_round(r, prec, flags, r->len, 0); } /* for debugging */ @@ -780,51 +782,26 @@ int bf_cmp_full(const bf_t *a, const bf_t *b) return res; } -#define BF_CMP_EQ 1 -#define BF_CMP_LT 2 -#define BF_CMP_LE 3 - -static int bf_cmp(const bf_t *a, const bf_t *b, int op) +/* Standard floating point comparison: return 2 if one of the operands + is NaN (unordered) or -1, 0, 1 depending on the ordering assuming + -0 == +0 */ +int bf_cmp(const bf_t *a, const bf_t *b) { - BOOL is_both_zero; int res; - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) - return 0; - if (a->sign != b->sign) { - is_both_zero = (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO); - if (is_both_zero) { - return op & BF_CMP_EQ; - } else if (op & BF_CMP_LT) { - return a->sign; - } else { - return FALSE; - } + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + res = 2; + } else if (a->sign != b->sign) { + if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) + res = 0; + else + res = 1 - 2 * a->sign; } else { res = bf_cmpu(a, b); - if (res == 0) { - return op & BF_CMP_EQ; - } else if (op & BF_CMP_LT) { - return (res < 0) ^ a->sign; - } else { - return FALSE; - } + if (a->sign) + res = -res; } -} - -int bf_cmp_eq(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b, BF_CMP_EQ); -} - -int bf_cmp_le(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b, BF_CMP_LE); -} - -int bf_cmp_lt(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b, BF_CMP_LT); + return res; } /* Compute the number of bits 'n' matching the pattern: @@ -1631,11 +1608,11 @@ int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) slimb_t e_max; if (r->len == 0) return 0; - e_max = ((limb_t)1 << BF_EXP_BITS_MAX) - 1; + e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; e = bf_max(e, -e_max); e = bf_min(e, e_max); r->expn += e; - return __bf_round(r, prec, flags, r->len); + return __bf_round(r, prec, flags, r->len, 0); } /* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, @@ -1777,8 +1754,7 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, } q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU); + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); switch(rnd_mode) { default: case BF_RNDZ: @@ -1792,12 +1768,12 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, case BF_RNDU: is_ceil = q_sign ^ 1; break; + case BF_RNDA: + is_ceil = TRUE; + break; case BF_DIVREM_EUCLIDIAN: is_ceil = a->sign; break; - case BF_RNDNU: - /* XXX: unsupported yet */ - abort(); } a1->expn = a->expn; @@ -1845,26 +1821,14 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, return BF_ST_MEM_ERROR; } -int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) { bf_t q_s, *q = &q_s; int ret; bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDZ); - bf_delete(q); - return ret; -} - -int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - bf_t q_s, *q = &q_s; - int ret; - - bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); bf_delete(q); return ret; } @@ -1879,13 +1843,13 @@ static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) } int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) + bf_flags_t flags, int rnd_mode) { bf_t q_s, *q = &q_s; int ret; bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); bf_get_limb(pq, q, BF_GET_INT_MOD); bf_delete(q); return ret; @@ -2791,14 +2755,14 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* XXX: correct overflow/underflow handling */ /* XXX: rigorous error analysis needed */ extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN); + ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); overflow = !bf_is_finite(B); /* XXX: if bf_pow_ui_ui returns an exact result, can stop after the next operation */ if (expn_sign) - ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN); + ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); else - ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN); + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); if (ret & BF_ST_MEM_ERROR) break; if ((ret & BF_ST_INEXACT) && @@ -2807,6 +2771,8 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* and more precision and retry */ ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); } else { + /* XXX: need to use __bf_round() to pass the inexact + flag for the subnormal case */ ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); break; } @@ -3062,7 +3028,7 @@ static int bf_atof_internal(bf_t *r, slimb_t *pexponent, c = to_digit(*p); if (c >= 10) break; - if (unlikely(expn > ((EXP_MAX - 2 - 9) / 10))) { + if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { /* exponent overflow */ if (exp_is_neg) { bf_set_zero(r, is_neg); @@ -3501,11 +3467,14 @@ static int bf_convert_to_radix(bf_t *r, slimb_t *pE, prec = prec0 + ziv_extra_bits; /* XXX: rigorous error analysis needed */ extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, BF_RNDN); + ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); if (!e_sign) - ret |= bf_mul(r, r, a, prec + extra_bits, BF_RNDN); + ret |= bf_mul(r, r, a, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); else - ret |= bf_div(r, a, r, prec + extra_bits, BF_RNDN); + ret |= bf_div(r, a, r, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); if (ret & BF_ST_MEM_ERROR) return BF_ST_MEM_ERROR; /* if the result is not exact, check that it can be safely @@ -3605,7 +3574,15 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, pos = a->len; pos_incr = 1; first_buf_pos = 0; - } else if ((radix & (radix - 1)) != 0) { + } else if ((radix & (radix - 1)) == 0) { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + /* digits are aligned relative to the radix point */ + pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); + first_buf_pos = 0; + } else { limb_t n, radixl; digits_per_limb = digits_per_limb_table[radix - 2]; @@ -3619,13 +3596,6 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, pos = n; pos_incr = 1; first_buf_pos = pos * digits_per_limb - n_digits; - } else { - a = (bf_t *)a1; - radix_bits = ceil_log2(radix); - digits_per_limb = LIMB_BITS / radix_bits; - pos_incr = digits_per_limb * radix_bits; - pos = a->len * LIMB_BITS - a->expn + n_digits * radix_bits; - first_buf_pos = 0; } buf_pos = digits_per_limb; i = 0; @@ -3668,12 +3638,13 @@ static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, limb_t prec, bf_flags_t flags, BOOL is_dec) { + bf_context_t *ctx = a2->ctx; DynBuf s_s, *s = &s_s; int radix_bits; // bf_print_str("ftoa", a2); // printf("radix=%d\n", radix); - dbuf_init2(s, a2->ctx, bf_dbuf_realloc); + dbuf_init2(s, ctx, bf_dbuf_realloc); if (a2->expn == BF_EXP_NAN) { dbuf_putstr(s, "NaN"); } else { @@ -3687,77 +3658,160 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, } else { int fmt, ret; slimb_t n_digits, n, i, n_max, n1; - bf_t a1_s, *a1; - bf_t a_s, *a = &a_s; - - /* make a positive number */ - a->tab = a2->tab; - a->len = a2->len; - a->expn = a2->expn; - a->sign = 0; - + bf_t a1_s, *a1 = &a1_s; + if ((radix & (radix - 1)) != 0) radix_bits = 0; else radix_bits = ceil_log2(radix); fmt = flags & BF_FTOA_FORMAT_MASK; - a1 = &a1_s; - bf_init(a2->ctx, a1); + bf_init(ctx, a1); if (fmt == BF_FTOA_FORMAT_FRAC) { - size_t pos, start; - assert(!is_dec); - /* one more digit for the rounding */ - n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); - n_digits = n + prec; - n1 = n; - if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, - flags & BF_RND_MASK, TRUE)) - goto fail1; - start = s->size; - output_digits(s, a1, radix, n_digits, n, is_dec); - /* remove leading zeros because we allocated one more digit */ - pos = start; - while ((pos + 1) < s->size && s->buf[pos] == '0' && - s->buf[pos + 1] != '.') - pos++; - if (pos > start) { - memmove(s->buf + start, s->buf + pos, s->size - pos); - s->size -= (pos - start); + if (is_dec || radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; +#ifdef USE_BF_DEC + if (is_dec) { + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = a1->expn; + } else +#endif + { + if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = ceil_div(a1->expn, radix_bits); + } + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) { + dbuf_putstr(s, "0"); + if (prec > 0) { + dbuf_putstr(s, "."); + for(i = 0; i < prec; i++) { + dbuf_putc(s, '0'); + } + } + } else { + n_digits = prec + n; + if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + if (n_digits > 0) { + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } else { + size_t pos, start; + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + /* one more digit for the rounding */ + n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); + n_digits = n + prec; + n1 = n; + if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, + flags & BF_RND_MASK, TRUE)) + goto fail1; + start = s->size; + output_digits(s, a1, radix, n_digits, n, is_dec); + /* remove leading zeros because we allocated one more digit */ + pos = start; + while ((pos + 1) < s->size && s->buf[pos] == '0' && + s->buf[pos + 1] != '.') + pos++; + if (pos > start) { + memmove(s->buf + start, s->buf + pos, s->size - pos); + s->size -= (pos - start); + } } } else { #ifdef USE_BF_DEC if (is_dec) { + if (bf_set(a1, a2)) + goto fail1; if (fmt == BF_FTOA_FORMAT_FIXED) { n_digits = prec; n_max = n_digits; + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; } else { /* prec is ignored */ - prec = n_digits = a->len * LIMB_DIGITS; - /* remove trailing zero digits */ + prec = n_digits = a1->len * LIMB_DIGITS; + /* remove the trailing zero digits */ while (n_digits > 1 && - get_digit(a->tab, a->len, prec - n_digits) == 0) { + get_digit(a1->tab, a1->len, prec - n_digits) == 0) { n_digits--; } n_max = n_digits + 4; } - bf_init(a2->ctx, a1); - bf_set(a1, a); n = a1->expn; } else #endif - { + if (radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + slimb_t prec_bits; + n_digits = prec; + n_max = n_digits; + /* align to the radix point */ + prec_bits = prec * radix_bits - + smod(-a1->expn, radix_bits); + if (bf_round(a1, prec_bits, + (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + limb_t digit_mask; + slimb_t pos; + /* position of the digit before the most + significant digit in bits */ + pos = a1->len * LIMB_BITS + + smod(-a1->expn, radix_bits); + n_digits = ceil_div(pos, radix_bits); + /* remove the trailing zero digits */ + digit_mask = ((limb_t)1 << radix_bits) - 1; + while (n_digits > 1 && + (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = ceil_div(a1->expn, radix_bits); + } else { + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + if (fmt == BF_FTOA_FORMAT_FIXED) { n_digits = prec; n_max = n_digits; } else { slimb_t n_digits_max, n_digits_min; - if (prec == BF_PREC_INF) { - assert(radix_bits != 0); - /* XXX: could use the exact number of bits */ - prec = a->len * LIMB_BITS; - } + assert(prec != BF_PREC_INF); n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); /* max number of digits for non exponential notation. The rational is to have the same rule @@ -3771,7 +3825,7 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, /* XXX: inefficient */ n_digits_max = n_digits; n_digits_min = 1; - bf_init(a2->ctx, b); + bf_init(ctx, b); while (n_digits_min < n_digits_max) { n_digits = (n_digits_min + n_digits_max) / 2; if (bf_convert_to_radix(a1, &n, a, radix, n_digits, @@ -3869,7 +3923,7 @@ static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, *plen = s->size - 1; return (char *)s->buf; fail: - bf_free(a2->ctx, s->buf); + bf_free(ctx, s->buf); if (plen) *plen = 0; return NULL; @@ -4035,7 +4089,7 @@ static void bf_const_pi_internal(bf_t *Q, limb_t prec) static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, BFConstCache *c, - void (*func)(bf_t *res, limb_t prec)) + void (*func)(bf_t *res, limb_t prec), int sign) { limb_t ziv_extra_bits, prec1; @@ -4051,6 +4105,7 @@ static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, prec1 = c->prec; } bf_set(T, &c->val); + T->sign = sign; if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { /* and more precision and retry */ ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); @@ -4070,13 +4125,20 @@ static void bf_const_free(BFConstCache *c) int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) { bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal); + return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); } -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) +/* return rounded pi * (1 - 2 * sign) */ +static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) { bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal); + return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, + sign); +} + +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) +{ + return bf_const_pi_signed(T, 0, prec, flags); } void bf_clear_cache(bf_context_t *s) @@ -4126,9 +4188,29 @@ static int bf_ziv_rounding(bf_t *r, const bf_t *a, break; } ziv_extra_bits = ziv_extra_bits * 2; + // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); } } - return bf_round(r, prec, flags) | ret; + if (r->len == 0) + return ret; + else + return __bf_round(r, prec, flags, r->len, ret); +} + +/* add (1 - 2*e_sign) * 2^e */ +static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, + limb_t prec, int flags) +{ + bf_t T_s, *T = &T_s; + int ret; + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_init(a->ctx, T); + bf_set_ui(T, 1); + T->sign = e_sign; + T->expn += e; + ret = bf_add(r, r, T, prec, flags); + bf_delete(T); + return ret; } /* Compute the exponential using faithful rounding at precision 'prec'. @@ -4193,18 +4275,70 @@ static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) /* undo the range reduction */ for(i = 0; i < K; i++) { - bf_mul(r, r, r, prec1, BF_RNDN); + bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); } /* undo the argument reduction */ - bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ); + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); return BF_ST_INEXACT; } +/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ +static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, + const bf_t *a_low, const bf_t *a_high, + limb_t prec, bf_flags_t flags) +{ + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + + if (a_high->expn <= 0) + return 0; + + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_max + 3; + if (flags & BF_FLAG_SUBNORMAL) + e_min -= (prec - 1); + + bf_init(s, T); + bf_init(s, log2); + bf_const_log2(log2, LIMB_BITS, BF_RNDU); + bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); + /* a_low > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a_low) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ + bf_const_log2(log2, LIMB_BITS, BF_RNDD); + bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a_high, T)) { + int rnd_mode = flags & BF_RND_MASK; + + /* underflow */ + bf_delete(T); + bf_delete(log2); + if (rnd_mode == BF_RNDU) { + /* set the smallest value */ + bf_set_ui(r, 1); + r->expn = e_min; + } else { + bf_set_zero(r, 0); + } + return BF_ST_UNDERFLOW | BF_ST_INEXACT; + } + bf_delete(log2); + bf_delete(T); + return 0; +} + int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { bf_context_t *s = r->ctx; + int ret; assert(r != a); if (a->len == 0) { if (a->expn == BF_EXP_NAN) { @@ -4220,48 +4354,15 @@ int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) return 0; } - /* crude overflow and underflow tests */ - if (a->expn > 0) { - bf_t T_s, *T = &T_s; - bf_t log2_s, *log2 = &log2_s; - slimb_t e_min, e_max; - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_max + 3; - if (flags & BF_FLAG_SUBNORMAL) - e_min -= (prec - 1); - - bf_init(s, T); - bf_init(s, log2); - bf_const_log2(log2, LIMB_BITS, BF_RNDU); - bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); - /* a > e_max * log(2) implies exp(a) > e_max */ - if (bf_cmp_lt(T, a) > 0) { - /* overflow */ - bf_delete(T); - bf_delete(log2); - return bf_set_overflow(r, 0, prec, flags); - } - /* a < e_min * log(2) implies exp(a) < e_min */ - bf_mul_si(T, log2, e_min, LIMB_BITS, BF_RNDD); - if (bf_cmp_lt(a, T)) { - int rnd_mode = flags & BF_RND_MASK; - - /* underflow */ - bf_delete(T); - bf_delete(log2); - if (rnd_mode == BF_RNDU) { - /* set the smallest value */ - bf_set_ui(r, 1); - r->expn = e_min; - } else { - bf_set_zero(r, 0); - } - return BF_ST_UNDERFLOW | BF_ST_INEXACT; - } - bf_delete(log2); - bf_delete(T); + ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); + if (ret) + return ret; + if (a->expn < 0 && (-a->expn) >= (prec + 2)) { + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); } - + return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); } @@ -4404,7 +4505,6 @@ int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) } /* x and y finite and x > 0 */ -/* XXX: overflow/underflow handling */ static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) { bf_context_t *s = r->ctx; @@ -4415,15 +4515,17 @@ static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) bf_init(s, T); /* XXX: proof for the added precision */ prec1 = prec + 32; - bf_log(T, x, prec1, BF_RNDF); - bf_mul(T, T, y, prec1, BF_RNDF); - bf_exp(r, T, prec1, BF_RNDF); + bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + if (bf_is_nan(T)) + bf_set_nan(r); + else + bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ bf_delete(T); return BF_ST_INEXACT; } /* x and y finite, x > 0, y integer and y fits on one limb */ -/* XXX: overflow/underflow handling */ static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) { bf_context_t *s = r->ctx; @@ -4438,11 +4540,11 @@ static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) y1 = -y1; /* XXX: proof for the added precision */ prec1 = prec + ceil_log2(y1) * 2 + 8; - ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN); + ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); if (y->sign) { bf_init(s, T); bf_set_ui(T, 1); - ret |= bf_div(r, T, r, prec1, BF_RNDN); + ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); bf_delete(T); } return ret; @@ -4508,7 +4610,7 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) int cmp_x_abs_1; bf_set_ui(r, 1); cmp_x_abs_1 = bf_cmpu(x, r); - if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUICKS) && + if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && (y->expn >= BF_EXP_INF)) { bf_set_nan(r); } else if (cmp_x_abs_1 == 0 && @@ -4565,70 +4667,95 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) if (bf_cmp_eq(T, r)) { /* abs(x) = 1: nothing more to do */ ret = 0; - } else if (y_is_int) { - slimb_t T_bits, e; - int_pow: - T_bits = T->expn - bf_get_exp_min(T); - if (T_bits == 1) { - /* pow(2^b, y) = 2^(b*y) */ - bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, T, 0); - bf_set_ui(r, 1); - ret = bf_mul_2exp(r, e, prec, flags); - } else if (prec == BF_PREC_INF) { - slimb_t y1; - /* specific case for infinite precision (integer case) */ - bf_get_limb(&y1, y, 0); - assert(!y->sign); - /* x must be an integer, so abs(x) >= 2 */ - if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { - bf_delete(T); - return bf_set_overflow(r, 0, BF_PREC_INF, flags); - } - ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); - } else { - if (y->expn <= 31) { - /* small enough power: use exponentiation in all cases */ - } else if (y->sign) { - /* cannot be exact */ - goto general_case; + } else { + /* check the overflow/underflow cases */ + { + bf_t al_s, *al = &al_s; + bf_t ah_s, *ah = &ah_s; + limb_t precl = LIMB_BITS; + + bf_init(s, al); + bf_init(s, ah); + /* compute bounds of log(abs(x)) * y with a low precision */ + /* XXX: compute bf_log() once */ + /* XXX: add a fast test before this slow test */ + bf_log(al, T, precl, BF_RNDD); + bf_log(ah, T, precl, BF_RNDU); + bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); + bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); + ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); + bf_delete(al); + bf_delete(ah); + if (ret) + goto done; + } + + if (y_is_int) { + slimb_t T_bits, e; + int_pow: + T_bits = T->expn - bf_get_exp_min(T); + if (T_bits == 1) { + /* pow(2^b, y) = 2^(b*y) */ + bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, T, 0); + bf_set_ui(r, 1); + ret = bf_mul_2exp(r, e, prec, flags); + } else if (prec == BF_PREC_INF) { + slimb_t y1; + /* specific case for infinite precision (integer case) */ + bf_get_limb(&y1, y, 0); + assert(!y->sign); + /* x must be an integer, so abs(x) >= 2 */ + if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { + bf_delete(T); + return bf_set_overflow(r, 0, BF_PREC_INF, flags); + } + ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); } else { - if (rnd_mode == BF_RNDF) - goto general_case; /* no need to track exact results */ - /* see if the result has a chance to be exact: - if x=a*2^b (a odd), x^y=a^y*2^(b*y) - x^y needs a precision of at least floor_log2(a)*y bits - */ - bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, r, 0); - if (prec < e) + if (y->expn <= 31) { + /* small enough power: use exponentiation in all cases */ + } else if (y->sign) { + /* cannot be exact */ goto general_case; + } else { + if (rnd_mode == BF_RNDF) + goto general_case; /* no need to track exact results */ + /* see if the result has a chance to be exact: + if x=a*2^b (a odd), x^y=a^y*2^(b*y) + x^y needs a precision of at least floor_log2(a)*y bits + */ + bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, r, 0); + if (prec < e) + goto general_case; + } + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); } - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); - } - } else { - if (rnd_mode != BF_RNDF) { - bf_t *y1; - if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { - /* the problem is reduced to a power to an integer */ + } else { + if (rnd_mode != BF_RNDF) { + bf_t *y1; + if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { + /* the problem is reduced to a power to an integer */ #if 0 - printf("\nn=%ld\n", -y_emin); - bf_print_str("T", T); - bf_print_str("r", r); + printf("\nn=%" PRId64 "\n", -(int64_t)y_emin); + bf_print_str("T", T); + bf_print_str("r", r); #endif - bf_set(T, r); - y1 = &ytmp_s; - y1->tab = y->tab; - y1->len = y->len; - y1->sign = y->sign; - y1->expn = y->expn - y_emin; - y = y1; - goto int_pow; + bf_set(T, r); + y1 = &ytmp_s; + y1->tab = y->tab; + y1->len = y->len; + y1->sign = y->sign; + y1->expn = y->expn - y_emin; + y = y1; + goto int_pow; + } } + general_case: + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); } - general_case: - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); } + done: bf_delete(T); r->sign = r_sign; return ret; @@ -4649,7 +4776,7 @@ static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) bf_delete(T); } -int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) +static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) { bf_context_t *s1 = a->ctx; bf_t T_s, *T = &T_s; @@ -4659,27 +4786,6 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) int is_neg; assert(c != a && s != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - if (c) - bf_set_nan(c); - if (s) - bf_set_nan(s); - return 0; - } else if (a->expn == BF_EXP_INF) { - if (c) - bf_set_nan(c); - if (s) - bf_set_nan(s); - return BF_ST_INVALID_OP; - } else { - if (c) - bf_set_ui(c, 1); - if (s) - bf_set_zero(s, a->sign); - return 0; - } - } bf_init(s1, T); bf_init(s1, U); @@ -4699,10 +4805,10 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) slimb_t cancel; cancel = 0; for(;;) { - prec2 = prec1 + cancel; + prec2 = prec1 + a->expn + cancel; bf_const_pi(U, prec2, BF_RNDF); bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); - bf_remquo(&mod, T, a, U, prec2, BF_RNDN); + bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); if (mod == 0 || (T->expn != BF_EXP_ZERO && (T->expn + prec2) >= (prec1 - 1))) @@ -4774,6 +4880,30 @@ static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_ui(r, 1); + return 0; + } + } + + /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + + O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = 2 * a->expn - 1; + if (e < -(prec + 2)) { + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, e, 1, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); } @@ -4784,15 +4914,6 @@ static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { - return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); -} - -static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - limb_t prec1; - if (a->len == 0) { if (a->expn == BF_EXP_NAN) { bf_set_nan(r); @@ -4806,6 +4927,26 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) } } + /* small argument case: result = x+r(x) with r(x) = -x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); +} + +static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + limb_t prec1; + /* XXX: precision analysis */ prec1 = prec + 8; bf_init(s, T); @@ -4817,6 +4958,31 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); } @@ -4834,50 +5000,15 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, int cmp_1; slimb_t prec1, i, K, l; - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else { - if (a->expn == BF_EXP_INF) - i = 1 - 2 * a->sign; - else - i = 0; - i += add_pi2; - /* return i*(pi/2) with -1 <= i <= 2 */ - if (i == 0) { - bf_set_zero(r, add_pi2 ? 0 : a->sign); - return 0; - } else { - /* PI or PI/2 */ - bf_const_pi(r, prec, BF_RNDF); - if (i != 2) - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - r->sign = (i < 0); - return BF_ST_INEXACT; - } - } - } - - bf_init(s, T); - bf_set_ui(T, 1); - cmp_1 = bf_cmpu(a, T); - if (cmp_1 == 0 && !add_pi2) { - /* short cut: abs(a) == 1 -> +/-pi/4 */ - bf_const_pi(r, prec, BF_RNDF); - bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); - r->sign = a->sign; - bf_delete(T); - return BF_ST_INEXACT; - } - /* XXX: precision analysis */ K = bf_isqrt((prec + 1) / 2); l = prec / (2 * K) + 1; prec1 = prec + K + 2 * l + 32; - // printf("prec=%ld K=%ld l=%ld prec1=%ld\n", prec, K, l, prec1); + // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); - if (cmp_1 > 0) { + bf_init(s, T); + cmp_1 = (a->expn >= 1); /* a >= 1 */ + if (cmp_1) { bf_set_ui(T, 1); bf_div(T, T, a, prec1, BF_RNDN); } else { @@ -4945,6 +5076,47 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + /* -PI/2 or PI/2 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res == 0) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); } @@ -4998,38 +5170,6 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) BOOL is_acos = (BOOL)(intptr_t)opaque; bf_t T_s, *T = &T_s; limb_t prec1, prec2; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - if (is_acos) { - bf_const_pi(r, prec, BF_RNDF); - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - } - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - if (res > 0) { - bf_delete(T); - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else if (res == 0 && a->sign == 0 && is_acos) { - bf_set_zero(r, 0); - bf_delete(T); - return 0; - } /* asin(x) = atan(x/sqrt(1-x^2)) acos(x) = pi/2 - asin(x) */ @@ -5041,6 +5181,7 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) prec2 = BF_PREC_INF; else prec2 = prec1; + bf_init(s, T); bf_mul(T, a, a, prec2, BF_RNDN); bf_neg(T); bf_add_si(T, T, 1, prec2, BF_RNDN); @@ -5056,11 +5197,76 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + + /* small argument case: result = x+r(x) with r(x) = x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); } int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_const_pi(r, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0) { + bf_set_zero(r, 0); + return 0; + } + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); } @@ -5124,14 +5330,14 @@ int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) #endif /* LIMB_BITS != 64 */ -static inline limb_t shrd(limb_t low, limb_t high, long shift) +static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) { if (shift != 0) low = (low >> shift) | (high << (LIMB_BITS - shift)); return low; } -static inline limb_t shld(limb_t a1, limb_t a0, long shift) +static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) { if (shift != 0) return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); @@ -5185,21 +5391,18 @@ do {\ /* fast integer division by a fixed constant */ typedef struct FastDivData { - limb_t d; /* divisor (only user visible field) */ limb_t m1; /* multiplier */ - int shift1; - int shift2; + int8_t shift1; + int8_t shift2; } FastDivData; -#if 1 /* From "Division by Invariant Integers using Multiplication" by Torborn Granlund and Peter L. Montgomery */ /* d must be != 0 */ -static inline void fast_udiv_init(FastDivData *s, limb_t d) +static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) { int l; limb_t q, r, m1; - s->d = d; if (d == 1) l = 0; else @@ -5225,52 +5428,78 @@ static inline limb_t fast_udiv(limb_t a, const FastDivData *s) return (t1 + t0) >> s->shift2; } -static inline limb_t fast_urem(limb_t a, const FastDivData *s) -{ - limb_t q; - q = fast_udiv(a, s); - return a - q * s->d; -} +/* contains 10^i */ +const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; -#define fast_udivrem(q, r, a, s) q = fast_udiv(a, s), r = a - q * (s)->d - +/* precomputed from fast_udiv_init(10^i) */ +static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { +#if LIMB_BITS == 32 + { 0x00000001, 0, 0 }, + { 0x9999999a, 1, 3 }, + { 0x47ae147b, 1, 6 }, + { 0x0624dd30, 1, 9 }, + { 0xa36e2eb2, 1, 13 }, + { 0x4f8b588f, 1, 16 }, + { 0x0c6f7a0c, 1, 19 }, + { 0xad7f29ac, 1, 23 }, + { 0x5798ee24, 1, 26 }, + { 0x12e0be83, 1, 29 }, #else - -static inline void fast_udiv_init(FastDivData *s, limb_t d) -{ - s->d = d; -} -static inline limb_t fast_udiv(limb_t a, const FastDivData *s) -{ - return a / s->d; -} -static inline limb_t fast_urem(limb_t a, const FastDivData *s) -{ - return a % s->d; -} - -#define fast_udivrem(q, r, a, s) q = a / (s)->d, r = a % (s)->d - + { 0x0000000000000001, 0, 0 }, + { 0x999999999999999a, 1, 3 }, + { 0x47ae147ae147ae15, 1, 6 }, + { 0x0624dd2f1a9fbe77, 1, 9 }, + { 0xa36e2eb1c432ca58, 1, 13 }, + { 0x4f8b588e368f0847, 1, 16 }, + { 0x0c6f7a0b5ed8d36c, 1, 19 }, + { 0xad7f29abcaf48579, 1, 23 }, + { 0x5798ee2308c39dfa, 1, 26 }, + { 0x12e0be826d694b2f, 1, 29 }, + { 0xb7cdfd9d7bdbab7e, 1, 33 }, + { 0x5fd7fe17964955fe, 1, 36 }, + { 0x19799812dea11198, 1, 39 }, + { 0xc25c268497681c27, 1, 43 }, + { 0x6849b86a12b9b01f, 1, 46 }, + { 0x203af9ee756159b3, 1, 49 }, + { 0xcd2b297d889bc2b7, 1, 53 }, + { 0x70ef54646d496893, 1, 56 }, + { 0x2725dd1d243aba0f, 1, 59 }, + { 0xd83c94fb6d2ac34d, 1, 63 }, #endif +}; -/* contains 10^i */ -/* XXX: make it const */ -limb_t mp_pow_dec[LIMB_DIGITS + 1]; -static FastDivData mp_pow_div[LIMB_DIGITS + 1]; - -static void mp_pow_init(void) +/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ +static inline limb_t fast_shr_dec(limb_t a, int shift) { - limb_t a; - int i; - - a = 1; - for(i = 0; i <= LIMB_DIGITS; i++) { - mp_pow_dec[i] = a; - fast_udiv_init(&mp_pow_div[i], a); - a = a * 10; - } + return fast_udiv(a, &mp_pow_div[shift]); } +/* division and remainder by 10^shift */ +#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] + limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, mp_size_t n, limb_t carry) { @@ -5653,7 +5882,7 @@ static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, l = high; for(i = n - 1; i >= 0; i--) { a = tab[i]; - fast_udivrem(q, r, a, &mp_pow_div[shift]); + fast_shr_rem_dec(q, r, a, shift); tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; l = r; } @@ -5671,7 +5900,7 @@ static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, l = low; for(i = 0; i < n; i++) { a = tab[i]; - fast_udivrem(q, r, a, &mp_pow_div[LIMB_DIGITS - shift]); + fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); tab_r[i] = r * mp_pow_dec[shift] + l; l = q; } @@ -6011,7 +6240,7 @@ static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) return 0; pos = (limb_t)bit_pos / LIMB_DIGITS; shift = (limb_t)bit_pos % LIMB_DIGITS; - fast_udivrem(q, v, r->tab[pos], &mp_pow_div[shift + 1]); + fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); (void)q; if (v != 0) return 1; @@ -6032,7 +6261,7 @@ static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) if (i < 0 || i >= len) return 0; shift = pos - i * LIMB_DIGITS; - return fast_udiv(tab[i], &mp_pow_div[shift]) % 10; + return fast_shr_dec(tab[i], shift) % 10; } #if 0 @@ -6056,7 +6285,7 @@ static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos) a1 = tab[i]; else a1 = 0; - return fast_udiv(a0, &mp_pow_div[shift]) + + return fast_shr_dec(a0, shift) + fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) * mp_pow_dec[shift]; } @@ -6108,13 +6337,8 @@ static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, case BF_RNDF: add_one = (digit1 >= 5); break; - case BF_RNDNU: - if (digit1 >= 5) { - if (r->sign) - add_one = (digit0 != 0); - else - add_one = 1; - } + case BF_RNDA: + add_one = inexact; break; default: abort(); @@ -6135,8 +6359,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) int shift, add_one, rnd_mode, ret; slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - /* e_min and e_max are computed to match the IEEE 754 conventions */ - /* XXX: does not matter for decimal numbers */ + /* XXX: align to IEEE 754 2008 for decimal numbers ? */ e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); e_min = -e_range + 3; e_max = e_range; @@ -6147,6 +6370,11 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) prec = r->expn + prec1; else prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); } else { prec = prec1; } @@ -6183,10 +6411,16 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) /* check underflow */ if (unlikely(r->expn < e_min)) { - underflow: - bfdec_set_zero(r, r->sign); - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + bfdec_set_zero(r, r->sign); + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } } /* check overflow */ @@ -6202,7 +6436,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) if (i >= 0) { shift = smod(bit_pos, LIMB_DIGITS); if (shift != 0) { - r->tab[i] = fast_udiv(r->tab[i], &mp_pow_div[shift]) * + r->tab[i] = fast_shr_dec(r->tab[i], shift) * mp_pow_dec[shift]; } } else { @@ -6571,10 +6805,10 @@ static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, /* number of digits after the decimal point */ /* XXX: check (2 extra digits for rounding + 2 digits) */ precl = (bf_max(a->expn - b->expn, 0) + 2 + - 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; } else { /* number of limbs of the quotient (2 extra digits for rounding) */ - precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; } n = bf_max(a->len, precl); @@ -6679,8 +6913,7 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, } q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU); + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); switch(rnd_mode) { default: case BF_RNDZ: @@ -6694,12 +6927,12 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, case BF_RNDU: is_ceil = q_sign ^ 1; break; + case BF_RNDA: + is_ceil = TRUE; + break; case BF_DIVREM_EUCLIDIAN: is_ceil = a->sign; break; - case BF_RNDNU: - /* XXX: unsupported yet */ - abort(); } a1->expn = a->expn; @@ -6756,14 +6989,14 @@ int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, return BF_ST_MEM_ERROR; } -int bfdec_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) { bfdec_t q_s, *q = &q_s; int ret; bfdec_init(r->ctx, q); - ret = bfdec_divrem(q, r, a, b, prec, flags, BF_RNDZ); + ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); bfdec_delete(q); return ret; } @@ -6779,7 +7012,7 @@ int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) bf_context_t *s = a->ctx; int ret, k; limb_t *a1, v; - slimb_t n, n1; + slimb_t n, n1, prec1; limb_t res; assert(r != a); @@ -6798,9 +7031,14 @@ int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) bfdec_set_nan(r); ret = BF_ST_INVALID_OP; } else { + if (flags & BF_FLAG_RADPNT_PREC) { + prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); + } else { + prec1 = prec; + } /* convert the mantissa to an integer with at least 2 * prec + 4 digits */ - n = (2 * (prec + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); + n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); if (bfdec_resize(r, n)) goto fail; a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); @@ -6869,7 +7107,7 @@ int bfdec_get_int32(int *pres, const bfdec_t *a) v = 0; ret = 0; } else if (a->expn <= 9) { - v = fast_udiv(a->tab[a->len - 1], &mp_pow_div[LIMB_DIGITS - a->expn]); + v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); if (a->sign) v = -v; ret = 0; @@ -6877,7 +7115,7 @@ int bfdec_get_int32(int *pres, const bfdec_t *a) uint64_t v1; uint32_t v_max; #if LIMB_BITS == 64 - v1 = fast_udiv(a->tab[a->len - 1], &mp_pow_div[LIMB_DIGITS - a->expn]); + v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); #else v1 = (uint64_t)a->tab[a->len - 1] * 10 + get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); @@ -41,8 +41,8 @@ typedef unsigned __int128 uint128_t; typedef int64_t slimb_t; typedef uint64_t limb_t; typedef uint128_t dlimb_t; -#define EXP_MIN INT64_MIN -#define EXP_MAX INT64_MAX +#define BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX #define LIMB_DIGITS 19 #define BF_DEC_BASE UINT64_C(10000000000000000000) @@ -52,8 +52,8 @@ typedef uint128_t dlimb_t; typedef int32_t slimb_t; typedef uint32_t limb_t; typedef uint64_t dlimb_t; -#define EXP_MIN INT32_MIN -#define EXP_MAX INT32_MAX +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX #define LIMB_DIGITS 9 #define BF_DEC_BASE 1000000000U @@ -61,10 +61,17 @@ typedef uint64_t dlimb_t; #endif /* in bits */ +/* minimum number of bits for the exponent */ #define BF_EXP_BITS_MIN 3 -#define BF_EXP_BITS_MAX (LIMB_BITS - 2) +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ #define BF_PREC_MIN 2 -#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2) +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ #define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ #if LIMB_BITS == 64 @@ -73,9 +80,9 @@ typedef uint64_t dlimb_t; #define BF_CHKSUM_MOD 975620677U #endif -#define BF_EXP_ZERO EXP_MIN -#define BF_EXP_INF (EXP_MAX - 1) -#define BF_EXP_NAN EXP_MAX +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX /* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, +/-infinity is represented with expn = BF_EXP_INF and len = 0, @@ -101,27 +108,29 @@ typedef struct { typedef enum { BF_RNDN, /* round to nearest, ties to even */ BF_RNDZ, /* round to zero */ - BF_RNDD, /* round to -inf */ + BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ BF_RNDU, /* round to +inf */ BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDNU, /* round to nearest, ties to +inf */ + BF_RNDA, /* round away from zero */ BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, inexact flag is always set) */ } bf_rnd_t; /* allow subnormal numbers. Only available if the number of exponent - bits is < BF_EXP_BITS_MAX and prec != BF_PREC_INF. Not supported - for decimal floating point numbers. */ + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ #define BF_FLAG_SUBNORMAL (1 << 3) /* 'prec' is the precision after the radix point instead of the whole - mantissa. Can only be used with bf_round(), bfdec_round() and - bfdev_div(). */ + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ #define BF_FLAG_RADPNT_PREC (1 << 4) #define BF_RND_MASK 0x7 #define BF_EXP_BITS_SHIFT 5 #define BF_EXP_BITS_MASK 0x3f +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + /* contains the rounding mode and number of exponents bits */ typedef uint32_t bf_flags_t; @@ -142,12 +151,17 @@ typedef struct bf_context_t { static inline int bf_get_exp_bits(bf_flags_t flags) { - return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK); + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; } static inline bf_flags_t bf_set_exp_bits(int n) { - return (BF_EXP_BITS_MAX - n) << BF_EXP_BITS_SHIFT; + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; } /* returned status */ @@ -249,9 +263,22 @@ int bf_set_float64(bf_t *a, double d); int bf_cmpu(const bf_t *a, const bf_t *b); int bf_cmp_full(const bf_t *a, const bf_t *b); -int bf_cmp_eq(const bf_t *a, const bf_t *b); -int bf_cmp_le(const bf_t *a, const bf_t *b); -int bf_cmp_lt(const bf_t *a, const bf_t *b); +int bf_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); @@ -264,12 +291,10 @@ int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags) #define BF_DIVREM_EUCLIDIAN BF_RNDF int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags, int rnd_mode); -int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); + bf_flags_t flags, int rnd_mode); /* round to integer with infinite precision */ int bf_rint(bf_t *r, int rnd_mode); int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); @@ -304,8 +329,8 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* Conversion of floating point number to string. Return a null terminated string or NULL if memory error. *plen contains its length if plen != NULL. The exponent letter is "e" for base 10, - "p" for bases 2, 8, 16 with the binary exponent and "@" for the - other bases. */ + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ #define BF_FTOA_FORMAT_MASK (3 << 16) @@ -316,17 +341,23 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* fractional format: prec digits after the decimal point rounded with (flags & BF_RND_MASK) */ #define BF_FTOA_FORMAT_FRAC (1 << 16) -/* free format: use as many digits as necessary so that bf_atof() - return the same number when using precision 'prec', rounding to - nearest and the subnormal+exponent configuration of 'flags'. The - result is meaningful only if 'a' is already rounded to the wanted - precision. +/* free format: + + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. - Infinite precision (BF_PREC_INF) is supported when the radix is a - power of two. */ + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ #define BF_FTOA_FORMAT_FREE (2 << 16) /* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). */ + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ #define BF_FTOA_FORMAT_FREE_MIN (3 << 16) /* force exponential notation for fixed or free format */ @@ -370,7 +401,7 @@ int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -#define BF_POW_JS_QUICKS (1 << 16) +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); @@ -448,17 +479,21 @@ static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) { return bf_cmp_full((const bf_t *)a, (const bf_t *)b); } +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) { - return bf_cmp_eq((const bf_t *)a, (const bf_t *)b); + return bfdec_cmp(a, b) == 0; } static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) { - return bf_cmp_le((const bf_t *)a, (const bf_t *)b); + return bfdec_cmp(a, b) <= 0; } static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) { - return bf_cmp_lt((const bf_t *)a, (const bf_t *)b); + return bfdec_cmp(a, b) < 0; } int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, @@ -475,8 +510,8 @@ int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags); int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int rnd_mode); -int bfdec_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); int bfdec_rint(bfdec_t *r, int rnd_mode); int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); @@ -488,7 +523,7 @@ int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, limb_t prec, bf_flags_t flags); /* the following functions are exported for testing only. */ -extern limb_t mp_pow_dec[LIMB_DIGITS + 1]; +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; void bfdec_print_str(const char *str, const bfdec_t *a); static inline int bfdec_resize(bfdec_t *r, limb_t len) { @@ -41,9 +41,6 @@ #include "cutils.h" #include "quickjs-libc.h" -/* enable bignums */ -#define CONFIG_BIGNUM - extern const uint8_t qjsc_repl[]; extern const uint32_t qjsc_repl_size; #ifdef CONFIG_BIGNUM @@ -258,12 +255,13 @@ static const JSMallocFunctions trace_mf = { void help(void) { printf("QuickJS version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [file]\n" + "usage: " PROG_NAME " [options] [file [args]]\n" "-h --help list options\n" "-e --eval EXPR evaluate EXPR\n" "-i --interactive go to interactive mode\n" "-m --module load as ES6 module (default=autodetect)\n" " --script load as ES6 script (default=autodetect)\n" + "-I --include file include an additional file\n" " --std make 'std' and 'os' available to the loaded script\n" #ifdef CONFIG_BIGNUM " --bignum enable the bignum extensions (BigFloat, BigDecimal)\n" @@ -292,6 +290,8 @@ int main(int argc, char **argv) int load_std = 0; int dump_unhandled_promise_rejection = 0; size_t memory_limit = 0; + char *include_list[32]; + int i, include_count = 0; #ifdef CONFIG_BIGNUM int load_jscalc, bignum_ext = 0; #endif @@ -345,6 +345,18 @@ int main(int argc, char **argv) fprintf(stderr, "qjs: missing expression for -e\n"); exit(2); } + if (opt == 'I' || !strcmp(longopt, "include")) { + if (optind >= argc) { + fprintf(stderr, "expecting filename"); + exit(1); + } + if (include_count >= countof(include_list)) { + fprintf(stderr, "too many included files"); + exit(1); + } + include_list[include_count++] = argv[optind++]; + continue; + } if (opt == 'i' || !strcmp(longopt, "interactive")) { interactive++; continue; @@ -426,6 +438,7 @@ int main(int argc, char **argv) if (bignum_ext || load_jscalc) { JS_AddIntrinsicBigFloat(ctx); JS_AddIntrinsicBigDecimal(ctx); + JS_AddIntrinsicOperators(ctx); JS_EnableBignumExt(ctx, TRUE); } #endif @@ -459,6 +472,11 @@ int main(int argc, char **argv) eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE); } + for(i = 0; i < include_count; i++) { + if (eval_file(ctx, include_list[i], module)) + goto fail; + } + if (expr) { if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0)) goto fail; @@ -36,9 +36,6 @@ #include "cutils.h" #include "quickjs-libc.h" -/* enable bignums */ -#define CONFIG_BIGNUM - typedef struct { char *name; char *short_name; @@ -79,7 +76,9 @@ static const FeatureEntry feature_list[] = { { "promise", "Promise" }, #define FE_MODULE_LOADER 9 { "module-loader", NULL }, +#ifdef CONFIG_BIGNUM { "bigint", "BigInt" }, +#endif }; void namelist_add(namelist_t *lp, const char *name, const char *short_name, @@ -485,9 +484,12 @@ int main(int argc, char **argv) FILE *fo; JSRuntime *rt; JSContext *ctx; - BOOL use_lto, bignum_ext; + BOOL use_lto; int module; OutputTypeEnum output_type; +#ifdef CONFIG_BIGNUM + BOOL bignum_ext = FALSE; +#endif out_filename = NULL; output_type = OUTPUT_EXECUTABLE; @@ -497,7 +499,6 @@ int main(int argc, char **argv) byte_swap = FALSE; verbose = 0; use_lto = FALSE; - bignum_ext = FALSE; /* add system modules */ namelist_add(&cmodule_list, "std", "std", 0); @@ -538,9 +539,13 @@ int main(int argc, char **argv) } if (i == countof(feature_list)) goto bad_feature; - } else if (!strcmp(optarg, "bignum")) { + } else +#ifdef CONFIG_BIGNUM + if (!strcmp(optarg, "bignum")) { bignum_ext = TRUE; - } else { + } else +#endif + { bad_feature: fprintf(stderr, "unsupported feature: %s\n", optarg); exit(1); @@ -615,6 +620,7 @@ int main(int argc, char **argv) if (bignum_ext) { JS_AddIntrinsicBigFloat(ctx); JS_AddIntrinsicBigDecimal(ctx); + JS_AddIntrinsicOperators(ctx); JS_EnableBignumExt(ctx, TRUE); } #endif @@ -661,13 +667,15 @@ int main(int argc, char **argv) feature_list[i].init_name); } } +#ifdef CONFIG_BIGNUM if (bignum_ext) { fprintf(fo, " JS_AddIntrinsicBigFloat(ctx);\n" " JS_AddIntrinsicBigDecimal(ctx);\n" + " JS_AddIntrinsicOperators(ctx);\n" " JS_EnableBignumExt(ctx, 1);\n"); } - +#endif fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n"); for(i = 0; i < init_module_list.count; i++) { @@ -1,7 +1,7 @@ /* * QuickJS Javascript Calculator * - * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2020 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,20 +27,8 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series, Matrix; (function(global) { - /* the types index are used to dispatch the operator functions */ - var OT_INT = 0; - var OT_FRACTION = 10; - var OT_FLOAT64 = 19; - var OT_FLOAT = 20; - var OT_COMPLEX = 30; - var OT_MOD = 40; - var OT_POLY = 50; - var OT_POLYMOD = 55; - var OT_RFUNC = 60; - var OT_SERIES = 70; - var OT_ARRAY = 80; - global.Integer = global.BigInt; + global.Float = global.BigFloat; global.algebraicMode = true; /* add non enumerable properties */ @@ -63,6 +51,42 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio Object.defineProperty(obj, prop, desc); } } + + /* same as proto[Symbol.operatorSet] = Operators.create(..op_list) + but allow shortcuts: left: [], right: [] or both + */ + function operators_set(proto, ...op_list) + { + var new_op_list, i, a, j, b, k, obj, tab; + var fields = [ "left", "right" ]; + new_op_list = []; + for(i = 0; i < op_list.length; i++) { + a = op_list[i]; + if (a.left || a.right) { + tab = [ a.left, a.right ]; + delete a.left; + delete a.right; + for(k = 0; k < 2; k++) { + obj = tab[k]; + if (obj) { + if (!Array.isArray(obj)) { + obj = [ obj ]; + } + for(j = 0; j < obj.length; j++) { + b = {}; + Object.assign(b, a); + b[fields[k]] = obj[j]; + new_op_list.push(b); + } + } + } + } else { + new_op_list.push(a); + } + } + proto[Symbol.operatorSet] = + Operators.create.call(null, ...new_op_list); + } /* Integer */ @@ -104,11 +128,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio var d, r, s, i, j, a; d = n - 1; s = 0; - while (d & 1) { + while ((d & 1) == 0) { d >>= 1; s++; } - t = Math.min(t, small_primes.length); + if (small_primes.length < t) + t = small_primes.length; loop: for(j = 0; j < t; j++) { a = small_primes[j]; r = Integer.pmod(a, d, n); @@ -140,25 +165,31 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return fact_rec(a, i) * fact_rec(i + 1, b); } } - + + /* math mode specific quirk to overload the integer division and power */ + Operators.updateBigIntOperators( + { + "/"(a, b) { + if (algebraicMode) { + return Fraction.toFraction(a, b); + } else { + return Float(a) / Float(b); + } + }, + "**"(a, b) { + if (algebraicMode) { + return generic_pow(a, b); + } else { + return Float(a) ** Float(b); + } + } + }); + add_props(Integer, { isInteger(a) { - return typeof a === "bigint"; - }, - [Symbol.operatorOrder]: OT_INT, - [Symbol.operatorDiv](a, b) { - if (algebraicMode) { - return Fraction.toFraction(a, b); - } else { - return Float(a) / Float(b); - } - }, - [Symbol.operatorPow](a, b) { - if (algebraicMode) { - return generic_pow(a, b); - } else { - return Float(a) ** Float(b); - } + /* integers are represented either as bigint or as number */ + return typeof a === "bigint" || + (typeof a === "number" && Number.isSafeInteger(a)); }, gcd(a, b) { var r; @@ -311,7 +342,10 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return this * this; }, abs() { - return Math.abs(this); + var v = this; + if (v < 0) + v = -v; + return v; }, conj() { return this; @@ -373,8 +407,119 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; } + function fraction_add(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den); + } + function fraction_sub(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den); + } + function fraction_mul(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.num, a.den * b.den); + } + function fraction_div(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den, a.den * b.num); + } + function fraction_mod(a, b) { + var a1 = Fraction(a); + var b1 = Fraction(b); + return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b; + } + function fraction_eq(a, b) { + a = Fraction(a); + b = Fraction(b); + /* we assume the fractions are normalized */ + return (a.num == b.num && a.den == b.den); + } + function fraction_lt(a, b) { + a = Fraction(a); + b = Fraction(b); + return (a.num * b.den < b.num * a.den); + } + + /* operators are needed for fractions */ + function float_add(a, b) { + return Float(a) + Float(b); + } + function float_sub(a, b) { + return Float(a) - Float(b); + } + function float_mul(a, b) { + return Float(a) * Float(b); + } + function float_div(a, b) { + return Float(a) / Float(b); + } + function float_mod(a, b) { + return Float(a) % Float(b); + } + function float_pow(a, b) { + return Float(a) ** Float(b); + } + function float_eq(a, b) { + /* XXX: may be better to use infinite precision for the comparison */ + return Float(a) === Float(b); + } + function float_lt(a, b) { + a = Float(a); + b = Float(b); + /* XXX: may be better to use infinite precision for the comparison */ + if (Float.isNaN(a) || Float.isNaN(b)) + return undefined; + else + return a < b; + } + + operators_set(Fraction.prototype, + { + "+": fraction_add, + "-": fraction_sub, + "*": fraction_mul, + "/": fraction_div, + "%": fraction_mod, + "**": generic_pow, + "==": fraction_eq, + "<": fraction_lt, + "pos"(a) { + return a; + }, + "neg"(a) { + return Fraction(-a.num, a.den); + }, + }, + { + left: [Number, BigInt], + right: [Number, BigInt], + "+": fraction_add, + "-": fraction_sub, + "*": fraction_mul, + "/": fraction_div, + "%": fraction_mod, + "**": generic_pow, + "==": fraction_eq, + "<": fraction_lt, + }, + { + left: Float, + right: Float, + "+": float_add, + "-": float_sub, + "*": float_mul, + "/": float_div, + "%": float_mod, + "**": float_pow, + "==": float_eq, + "<": float_lt, + }); + add_props(Fraction, { - [Symbol.operatorOrder]: OT_FRACTION, /* (internal use) simplify 'a' to an integer when possible */ toFraction(a, b) { var r = Fraction(a, b); @@ -383,72 +528,16 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio else return r; }, - - [Symbol.operatorAdd](a, b) { - a = Fraction(a); - b = Fraction(b); - return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den); - }, - [Symbol.operatorSub](a, b) { - a = Fraction(a); - b = Fraction(b); - return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den); - }, - [Symbol.operatorMul](a, b) { - a = Fraction(a); - b = Fraction(b); - return Fraction.toFraction(a.num * b.num, a.den * b.den); - }, - [Symbol.operatorDiv](a, b) { - a = Fraction(a); - b = Fraction(b); - return Fraction.toFraction(a.num * b.den, a.den * b.num); - }, - [Symbol.operatorMathMod](a, b) { - var a1 = Fraction(a); - var b1 = Fraction(b); - return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b; - }, - [Symbol.operatorMod](a, b) { - var a1 = Fraction(a); - var b1 = Fraction(b); - return a - Integer.tdiv(a1.num * b1.den, a1.den * b1.num) * b; - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - a = Fraction(a); - b = Fraction(b); - /* we assume the fractions are normalized */ - return (a.num == b.num && a.den == b.den); - }, - [Symbol.operatorCmpLT](a, b) { - a = Fraction(a); - b = Fraction(b); - return (a.num * b.den < b.num * a.den); - }, - [Symbol.operatorCmpLE](a, b) { - a = Fraction(a); - b = Fraction(b); - return (a.num * b.den <= b.num * a.den); - }, }); add_props(Fraction.prototype, { [Symbol.toPrimitive](hint) { - if (hint === "integer") { - return Integer.tdiv(this.num, this.den); - } else if (hint === "string") { + if (hint === "string") { return this.toString(); } else { return Float(this.num) / this.den; } }, - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return Fraction(-this.num, this.den); - }, inverse() { return Fraction(this.den, this.num); }, @@ -460,7 +549,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, abs() { if (this.num < 0) - return this[Symbol.operatorNeg](); + return -this; else return this; }, @@ -483,29 +572,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio /* Number (Float64) */ - add_props(Number, { - [Symbol.operatorOrder]: OT_FLOAT64, - /* operators are needed for fractions */ - [Symbol.operatorAdd](a, b) { - return Number(a) + Number(b); - }, - [Symbol.operatorSub](a, b) { - return Number(a) - Number(b); - }, - [Symbol.operatorMul](a, b) { - return Number(a) * Number(b); - }, - [Symbol.operatorDiv](a, b) { - return Number(a) / Number(b); - }, - [Symbol.operatorPow](a, b) { - return Number(a) ** Number(b); - }, - }); - add_props(Number.prototype, { inverse() { - return 1.0 / this; + return 1 / this; }, norm2() { return this * this; @@ -536,8 +605,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio /* Float */ - global.Float = global.BigFloat; - var const_tab = []; /* we cache the constants for small precisions */ @@ -603,26 +670,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio get SQRT2() { return get_const(7); }, }); - add_props(Float, { - [Symbol.operatorOrder]: OT_FLOAT, - /* operators are needed for fractions */ - [Symbol.operatorAdd](a, b) { - return Float(a) + Float(b); - }, - [Symbol.operatorSub](a, b) { - return Float(a) - Float(b); - }, - [Symbol.operatorMul](a, b) { - return Float(a) * Float(b); - }, - [Symbol.operatorDiv](a, b) { - return Float(a) / Float(b); - }, - [Symbol.operatorPow](a, b) { - return Float(a) ** Float(b); - }, - }); - add_props(Float.prototype, { inverse() { return 1.0 / this; @@ -631,7 +678,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return this * this; }, abs() { - return Math.abs(this); + return Float.abs(this); }, conj() { return this; @@ -672,8 +719,61 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; } + + function complex_add(a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re + b.re, a.im + b.im); + } + function complex_sub(a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re - b.re, a.im - b.im); + } + function complex_mul(a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re * b.re - a.im * b.im, + a.re * b.im + a.im * b.re); + } + function complex_div(a, b) { + a = Complex(a); + b = Complex(b); + return a * b.inverse(); + } + function complex_eq(a, b) { + a = Complex(a); + b = Complex(b); + return a.re == b.re && a.im == b.im; + } + + operators_set(Complex.prototype, + { + "+": complex_add, + "-": complex_sub, + "*": complex_mul, + "/": complex_div, + "**": generic_pow, + "==": complex_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return Complex(-a.re, -a.im); + } + }, + { + left: [Number, BigInt, Float, Fraction], + right: [Number, BigInt, Float, Fraction], + "+": complex_add, + "-": complex_sub, + "*": complex_mul, + "/": complex_div, + "**": generic_pow, + "==": complex_eq, + }); + add_props(Complex, { - [Symbol.operatorOrder]: OT_COMPLEX, /* simplify to real number when possible */ toComplex(re, im) { if (algebraicMode && im == 0) @@ -681,43 +781,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio else return Complex(re, im); }, - - [Symbol.operatorAdd](a, b) { - a = Complex(a); - b = Complex(b); - return Complex.toComplex(a.re + b.re, a.im + b.im); - }, - [Symbol.operatorSub](a, b) { - a = Complex(a); - b = Complex(b); - return Complex.toComplex(a.re - b.re, a.im - b.im); - }, - [Symbol.operatorMul](a, b) { - a = Complex(a); - b = Complex(b); - return Complex.toComplex(a.re * b.re - a.im * b.im, - a.re * b.im + a.im * b.re); - }, - [Symbol.operatorDiv](a, b) { - a = Complex(a); - b = Complex(b); - return a * b.inverse(); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - a = Complex(a); - b = Complex(b); - return a.re == b.re && a.im == b.im; - } }); add_props(Complex.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return Complex(-this.re, -this.im); - }, inverse() { var c = this.norm2(); return Complex(this.re / c, -this.im / c); @@ -785,14 +851,75 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio obj.mod = m; return obj; }; + + function mod_add(a, b) { + if (!(a instanceof Mod)) { + return Mod(a + b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res + b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res + b.res, a.mod); + } + } + function mod_sub(a, b) { + if (!(a instanceof Mod)) { + return Mod(a - b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res - b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res - b.res, a.mod); + } + } + function mod_mul(a, b) { + if (!(a instanceof Mod)) { + return Mod(a * b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res * b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res * b.res, a.mod); + } + } + function mod_div(a, b) { + if (!(b instanceof Mod)) + b = Mod(b, a.mod); + return mod_mul(a, b.inverse()); + } + function mod_eq(a, b) { + return (a.mod == b.mod && a.res == b.res); + } - add_props(Mod.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return Mod(-this.res, this.mod); + operators_set(Mod.prototype, + { + "+": mod_add, + "-": mod_sub, + "*": mod_mul, + "/": mod_div, + "**": generic_pow, + "==": mod_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return Mod(-a.res, a.mod); + } }, + { + left: [Number, BigInt, Float, Fraction], + right: [Number, BigInt, Float, Fraction], + "+": mod_add, + "-": mod_sub, + "*": mod_mul, + "/": mod_div, + "**": generic_pow, + }); + + add_props(Mod.prototype, { inverse() { var a = this, m = a.mod; if (Integer.isInteger(m)) { @@ -806,57 +933,21 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - add_props(Mod, { - [Symbol.operatorOrder]: OT_MOD, - [Symbol.operatorAdd](a, b) { - if (!(a instanceof Mod)) { - return Mod(a + b.res, b.mod); - } else if (!(b instanceof Mod)) { - return Mod(a.res + b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return Mod(a.res + b.res, a.mod); - } - }, - [Symbol.operatorSub](a, b) { - if (!(a instanceof Mod)) { - return Mod(a - b.res, b.mod); - } else if (!(b instanceof Mod)) { - return Mod(a.res - b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return Mod(a.res - b.res, a.mod); - } - }, - [Symbol.operatorMul](a, b) { - if (!(a instanceof Mod)) { - return Mod(a * b.res, b.mod); - } else if (!(b instanceof Mod)) { - return Mod(a.res * b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return Mod(a.res * b.res, a.mod); - } - }, - [Symbol.operatorDiv](a, b) { - if (!(b instanceof Mod)) - b = Mod(b, a.mod); - return Mod[Symbol.operatorMul](a, b.inverse()); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - if (!(a instanceof Mod) || - !(b instanceof Mod)) - return false; - return (a.mod == b.mod && a.res == b.res); - } - }); - /* Polynomial */ + function polynomial_is_scalar(a) + { + if (typeof a === "number" || + typeof a === "bigint" || + typeof a === "bigfloat") + return true; + if (a instanceof Fraction || + a instanceof Complex || + a instanceof Mod) + return true; + return false; + } + Polynomial = function Polynomial(a) { if (new.target) @@ -868,7 +959,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio a = [ 0 ]; Object.setPrototypeOf(a, Polynomial.prototype); return a.trim(); - } else if (a.constructor[Symbol.operatorOrder] <= OT_MOD) { + } else if (polynomial_is_scalar(a)) { a = [a]; Object.setPrototypeOf(a, Polynomial.prototype); return a; @@ -1004,18 +1095,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio a.length = i; return a; }, - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - var r, i, n, a; - a = this; - n = a.length; - r = []; - for(i = 0; i < n; i++) - r[i] = -a[i]; - return Polynomial(r); - }, conj() { var r, i, n, a; a = this; @@ -1097,74 +1176,106 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - add_props(Polynomial, { - [Symbol.operatorOrder]: OT_POLY, - [Symbol.operatorAdd](a, b) { - var tmp, r, i, n1, n2; - a = Polynomial(a); - b = Polynomial(b); - if (a.length < b.length) { - tmp = a; - a = b; - b = tmp; - } - n1 = b.length; - n2 = a.length; - r = []; - for(i = 0; i < n1; i++) - r[i] = a[i] + b[i]; - for(i = n1; i < n2; i++) - r[i] = a[i]; - return Polynomial(r); - }, - [Symbol.operatorSub](a, b) { - return Polynomial[Symbol.operatorAdd](a, -b); - }, - [Symbol.operatorMul](a, b) { - var i, j, n1, n2, n, r; - a = Polynomial(a); - b = Polynomial(b); - n1 = a.length; - n2 = b.length; - n = n1 + n2 - 1; - r = []; - for(i = 0; i < n; i++) - r[i] = 0; - for(i = 0; i < n1; i++) { - for(j = 0; j < n2; j++) { - r[i + j] += a[i] * b[j]; - } + + function polynomial_add(a, b) { + var tmp, r, i, n1, n2; + a = Polynomial(a); + b = Polynomial(b); + if (a.length < b.length) { + tmp = a; + a = b; + b = tmp; + } + n1 = b.length; + n2 = a.length; + r = []; + for(i = 0; i < n1; i++) + r[i] = a[i] + b[i]; + for(i = n1; i < n2; i++) + r[i] = a[i]; + return Polynomial(r); + } + function polynomial_sub(a, b) { + return polynomial_add(a, -b); + } + function polynomial_mul(a, b) { + var i, j, n1, n2, n, r; + a = Polynomial(a); + b = Polynomial(b); + n1 = a.length; + n2 = b.length; + n = n1 + n2 - 1; + r = []; + for(i = 0; i < n; i++) + r[i] = 0; + for(i = 0; i < n1; i++) { + for(j = 0; j < n2; j++) { + r[i + j] += a[i] * b[j]; } - return Polynomial(r); - }, - [Symbol.operatorDiv](a, b) { - if (b.constructor[Symbol.operatorOrder] <= OT_COMPLEX) - return a * (1 / b); - else - return RationalFunction(Polynomial(a), - Polynomial(b)); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorMathMod](a, b) { - return Polynomial.divrem(a, b)[1]; - }, - [Symbol.operatorMod](a, b) { - return Polynomial.divrem(a, b)[1]; - }, - [Symbol.operatorCmpEQ](a, b) { - var n, i; - if (!(a instanceof Polynomial) || - !(b instanceof Polynomial)) - return false; - n = a.length; - if (n != b.length) + } + return Polynomial(r); + } + function polynomial_div_scalar(a, b) { + return a * (1 / b); + } + function polynomial_div(a, b) + { + return RationalFunction(Polynomial(a), + Polynomial(b)); + } + function polynomial_mod(a, b) { + return Polynomial.divrem(a, b)[1]; + } + function polynomial_eq(a, b) { + var n, i; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) return false; - for(i = 0; i < n; i++) { - if (a[i] != b[i]) - return false; - } - return true; - }, + } + return true; + } + + operators_set(Polynomial.prototype, + { + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div, + "**": generic_pow, + "==": polynomial_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var r, i, n, a; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = -a[i]; + return Polynomial(r); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod], + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div, + "**": generic_pow, /* XXX: only for integer */ + }, + { + right: [Number, BigInt, Float, Fraction, Complex, Mod], + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div_scalar, + "**": generic_pow, /* XXX: only for integer */ + }); + + add_props(Polynomial, { divrem(a, b) { var n1, n2, i, j, q, r, n, c; if (b.deg() < 0) @@ -1252,13 +1363,66 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; }; + function polymod_add(a, b) { + if (!(a instanceof PolyMod)) { + return PolyMod(a + b.res, b.mod); + } else if (!(b instanceof PolyMod)) { + return PolyMod(a.res + b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return PolyMod(a.res + b.res, a.mod); + } + } + function polymod_sub(a, b) { + return polymod_add(a, -b); + } + function polymod_mul(a, b) { + if (!(a instanceof PolyMod)) { + return PolyMod(a * b.res, b.mod); + } else if (!(b instanceof PolyMod)) { + return PolyMod(a.res * b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return PolyMod(a.res * b.res, a.mod); + } + } + function polymod_div(a, b) { + if (!(b instanceof PolyMod)) + b = PolyMod(b, a.mod); + return polymod_mul(a, b.inverse()); + } + function polymod_eq(a, b) { + return (a.mod == b.mod && a.res == b.res); + } + + operators_set(PolyMod.prototype, + { + "+": polymod_add, + "-": polymod_sub, + "*": polymod_mul, + "/": polymod_div, + "**": generic_pow, + "==": polymod_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return PolyMod(-a.res, a.mod); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": polymod_add, + "-": polymod_sub, + "*": polymod_mul, + "/": polymod_div, + "**": generic_pow, /* XXX: only for integer */ + }); + add_props(PolyMod.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return PolyMod(-this.res, this.mod); - }, inverse() { var a = this, m = a.mod; if (m instanceof Polynomial) { @@ -1272,55 +1436,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - add_props(PolyMod, { - [Symbol.operatorOrder]: OT_POLYMOD, - [Symbol.operatorAdd](a, b) { - if (!(a instanceof PolyMod)) { - return PolyMod(a + b.res, b.mod); - } else if (!(b instanceof PolyMod)) { - return PolyMod(a.res + b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return PolyMod(a.res + b.res, a.mod); - } - }, - [Symbol.operatorSub](a, b) { - if (!(a instanceof PolyMod)) { - return PolyMod(a - b.res, b.mod); - } else if (!(b instanceof PolyMod)) { - return PolyMod(a.res - b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return PolyMod(a.res - b.res, a.mod); - } - }, - [Symbol.operatorMul](a, b) { - if (!(a instanceof PolyMod)) { - return PolyMod(a * b.res, b.mod); - } else if (!(b instanceof PolyMod)) { - return PolyMod(a.res * b, a.mod); - } else { - if (a.mod != b.mod) - throw TypeError("different modulo for binary operator"); - return PolyMod(a.res * b.res, a.mod); - } - }, - [Symbol.operatorDiv](a, b) { - if (!(b instanceof PolyMod)) - b = PolyMod(b, a.mod); - return PolyMod[Symbol.operatorMul](a, b.inverse()); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - if (!(a instanceof PolyMod) || - !(b instanceof PolyMod)) - return false; - return (a.mod == b.mod && a.res == b.res); - } - }); - /* Rational function */ RationalFunction = function RationalFunction(a, b) @@ -1347,12 +1462,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } add_props(RationalFunction.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return RationalFunction(-this.num, this.den); - }, inverse() { return RationalFunction(this.den, this.num); }, @@ -1378,8 +1487,59 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); + function ratfunc_add(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den); + } + function ratfunc_sub(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den); + } + function ratfunc_mul(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.num, a.den * b.den); + } + function ratfunc_div(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den, a.den * b.num); + } + function ratfunc_eq(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + /* we assume the fractions are normalized */ + return (a.num == b.num && a.den == b.den); + } + + operators_set(RationalFunction.prototype, + { + "+": ratfunc_add, + "-": ratfunc_sub, + "*": ratfunc_mul, + "/": ratfunc_div, + "**": generic_pow, + "==": ratfunc_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return RationalFunction(-this.num, this.den); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": ratfunc_add, + "-": ratfunc_sub, + "*": ratfunc_mul, + "/": ratfunc_div, + "**": generic_pow, /* should only be used with integers */ + }); + add_props(RationalFunction, { - [Symbol.operatorOrder]: OT_RFUNC, /* This function always return a RationalFunction object even if it could simplified to a polynomial, so it is not equivalent to RationalFunction(a) */ @@ -1394,33 +1554,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; } }, - [Symbol.operatorAdd](a, b) { - a = RationalFunction.toRationalFunction(a); - b = RationalFunction.toRationalFunction(b); - return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den); - }, - [Symbol.operatorSub](a, b) { - a = RationalFunction.toRationalFunction(a); - b = RationalFunction.toRationalFunction(b); - return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den); - }, - [Symbol.operatorMul](a, b) { - a = RationalFunction.toRationalFunction(a); - b = RationalFunction.toRationalFunction(b); - return RationalFunction(a.num * b.num, a.den * b.den); - }, - [Symbol.operatorDiv](a, b) { - a = RationalFunction.toRationalFunction(a); - b = RationalFunction.toRationalFunction(b); - return RationalFunction(a.num * b.den, a.den * b.num); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - a = RationalFunction.toRationalFunction(a); - b = RationalFunction.toRationalFunction(b); - /* we assume the fractions are normalized */ - return (a.num == b.num && a.den == b.den); - }, }); /* Power series */ @@ -1435,6 +1568,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } return n; }; + + function series_is_scalar_or_polynomial(a) + { + return polynomial_is_scalar(a) || + (a instanceof Polynomial); + } /* n is the maximum number of terms if 'a' is not a serie */ Series = function Series(a, n) { @@ -1442,7 +1581,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio if (a instanceof Series) { return a; - } else if (a.constructor[Symbol.operatorOrder] <= OT_POLY) { + } else if (series_is_scalar_or_polynomial(a)) { if (n <= 0) { /* XXX: should still use the polynomial degree */ return Series.zero(0, 0); @@ -1462,19 +1601,132 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } }; - add_props(Series.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - var obj, n, i; - n = this.length; - obj = Series.zero(this.length, this.emin); - for(i = 0; i < n; i++) { - obj[i] = -this[i]; + function series_add(v1, v2) { + var tmp, d, emin, n, r, i, j, v2_emin, c1, c2; + if (!(v1 instanceof Series)) { + tmp = v1; + v1 = v2; + v2 = tmp; + } + d = v1.emin + v1.length; + if (series_is_scalar_or_polynomial(v2)) { + v2 = Polynomial(v2); + if (d <= 0) + return v1; + v2_emin = 0; + } else if (v2 instanceof RationalFunction) { + /* compute the emin of the rational fonction */ + i = get_emin(v2.num) - get_emin(v2.den); + if (d <= i) + return v1; + /* compute the serie with the required terms */ + v2 = Series(v2, d - i); + v2_emin = v2.emin; + } else { + v2_emin = v2.emin; + d = Math.min(d, v2_emin + v2.length); + } + emin = Math.min(v1.emin, v2_emin); + n = d - emin; + r = Series.zero(n, emin); + /* XXX: slow */ + for(i = emin; i < d; i++) { + j = i - v1.emin; + if (j >= 0 && j < v1.length) + c1 = v1[j]; + else + c1 = 0; + j = i - v2_emin; + if (j >= 0 && j < v2.length) + c2 = v2[j]; + else + c2 = 0; + r[i - emin] = c1 + c2; + } + return r.trim(); + } + function series_sub(a, b) { + return series_add(a, -b); + } + function series_mul(v1, v2) { + var n, i, j, r, n, emin, n1, n2, k; + if (!(v1 instanceof Series)) + v1 = Series(v1, v2.length); + else if (!(v2 instanceof Series)) + v2 = Series(v2, v1.length); + emin = v1.emin + v2.emin; + n = Math.min(v1.length, v2.length); + n1 = v1.length; + n2 = v2.length; + r = Series.zero(n, emin); + for(i = 0; i < n1; i++) { + k = Math.min(n2, n - i); + for(j = 0; j < k; j++) { + r[i + j] += v1[i] * v2[j]; } - return obj; - }, + } + return r.trim(); + } + function series_div(v1, v2) { + if (!(v2 instanceof Series)) + v2 = Series(v2, v1.length); + return series_mul(v1, v2.inverse()); + } + function series_pow(a, b) { + if (Integer.isInteger(b)) { + return generic_pow(a, b); + } else { + if (!(a instanceof Series)) + a = Series(a, b.length); + return exp(log(a) * b); + } + } + function series_eq(a, b) { + var n, i; + if (a.emin != b.emin) + return false; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) + return false; + } + return true; + } + + operators_set(Series.prototype, + { + "+": series_add, + "-": series_sub, + "*": series_mul, + "/": series_div, + "**": series_pow, + "==": series_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var obj, n, i; + n = a.length; + obj = Series.zero(a.length, a.emin); + for(i = 0; i < n; i++) { + obj[i] = -a[i]; + } + return obj; + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": series_add, + "-": series_sub, + "*": series_mul, + "/": series_div, + "**": series_pow, + }); + + add_props(Series.prototype, { conj() { var obj, n, i; n = this.length; @@ -1610,7 +1862,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }); add_props(Series, { - [Symbol.operatorOrder]: OT_SERIES, /* new series of length n and first exponent emin */ zero(n, emin) { var r, i, obj; @@ -1624,108 +1875,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio Object.setPrototypeOf(r, obj); return r; }, - [Symbol.operatorAdd](v1, v2) { - var tmp, d, emin, n, r, i, j, v2_emin, c1, c2; - if (!(v1 instanceof Series)) { - tmp = v1; - v1 = v2; - v2 = tmp; - } - d = v1.emin + v1.length; - if (v2.constructor[Symbol.operatorOrder] <= OT_POLY) { - v2 = Polynomial(v2); - if (d <= 0) - return v1; - v2_emin = 0; - } else if (v2 instanceof RationalFunction) { - /* compute the emin of the rational fonction */ - i = get_emin(v2.num) - get_emin(v2.den); - if (d <= i) - return v1; - /* compute the serie with the required terms */ - v2 = Series(v2, d - i); - v2_emin = v2.emin; - } else { - v2_emin = v2.emin; - d = Math.min(d, v2_emin + v2.length); - } - emin = Math.min(v1.emin, v2_emin); - n = d - emin; - r = Series.zero(n, emin); - /* XXX: slow */ - for(i = emin; i < d; i++) { - j = i - v1.emin; - if (j >= 0 && j < v1.length) - c1 = v1[j]; - else - c1 = 0; - j = i - v2_emin; - if (j >= 0 && j < v2.length) - c2 = v2[j]; - else - c2 = 0; - r[i - emin] = c1 + c2; - } - return r.trim(); - }, - [Symbol.operatorSub](a, b) { - return Series[Symbol.operatorAdd](a, -b); - }, - [Symbol.operatorMul](v1, v2) { - var n, i, j, r, n, emin, n1, n2, k; - if (!(v1 instanceof Series)) - v1 = Series(v1, v2.length); - else if (!(v2 instanceof Series)) - v2 = Series(v2, v1.length); - emin = v1.emin + v2.emin; - n = Math.min(v1.length, v2.length); - n1 = v1.length; - n2 = v2.length; - r = Series.zero(n, emin); - for(i = 0; i < n1; i++) { - k = Math.min(n2, n - i); - for(j = 0; j < k; j++) { - r[i + j] += v1[i] * v2[j]; - } - } - return r.trim(); - }, - [Symbol.operatorDiv](v1, v2) { - if (!(v2 instanceof Series)) - v2 = Series(v2, v1.length); - return Series[Symbol.operatorMul](v1, v2.inverse()); - }, - [Symbol.operatorPow](a, b) { - if (Integer.isInteger(b)) { - return generic_pow(a, b); - } else { - if (!(a instanceof Series)) - a = Series(a, b.length); - return exp(log(a) * b); - } - }, - [Symbol.operatorCmpEQ](a, b) { - var n, i; - if (!(a instanceof Series) || - !(b instanceof Series)) - return false; - if (a.emin != b.emin) - return false; - n = a.length; - if (n != b.length) - return false; - for(i = 0; i < n; i++) { - if (a[i] != b[i]) - return false; - } - return true; - }, O(a) { function ErrorO() { return TypeError("invalid O() argument"); } var n; - if (a.constructor[Symbol.operatorOrder] <= OT_POLY) { + if (series_is_scalar_or_polynomial(a)) { a = Polynomial(a); n = a.deg(); if (n < 0) @@ -2061,130 +2216,138 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - add_props(Array, { - [Symbol.operatorOrder]: OT_ARRAY, - [Symbol.operatorAdd](a, b) { - var r, i, n; - if (!Array.isArray(a) || !Array.isArray(b)) - throw TypeError("array expected"); - n = a.length; - if (n != b.length) - throw TypeError("incompatible array size"); - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] + b[i]; - return r; - }, - [Symbol.operatorSub](a, b) { - var r, i, n; - n = a.length; - if (!Array.isArray(a) || !Array.isArray(b)) - throw TypeError("array expected"); - if (n != b.length) - throw TypeError("incompatible array size"); - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] - b[i]; - return r; - }, - scalar_mul(a, b) { - var r, i, n; - n = a.length; - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] * b; - return r; - }, - [Symbol.operatorMul](a, b) { - var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat, a_is_array, b_is_array; - a_is_array = Array.isArray(a); - b_is_array = Array.isArray(b); - if (!a_is_array && !b_is_array) { - throw TypeError("array expected"); - } else if (!a_is_array && b_is_array) { - return Array.scalar_mul(b, a); - } else if (a_is_array && !b_is_array) { - return Array.scalar_mul(a, b); - } - h = a.length; - a_mat = Array.isArray(a[0]); - if (a_mat) { - l = a[0].length; - } else { - l = 1; - } - if (l != b.length) - throw RangeError("incompatible matrix size"); - b_mat = Array.isArray(b[0]); - if (b_mat) - w = b[0].length; - else - w = 1; - r = []; - if (a_mat && b_mat) { - for(i = 0; i < h; i++) { - rl = []; - for(j = 0; j < w; j++) { - sum = 0; - for(k = 0; k < l; k++) { - sum += a[i][k] * b[k][j]; - } - rl[j] = sum; - } - r[i] = rl; - } - } else if (a_mat && !b_mat) { - for(i = 0; i < h; i++) { + function array_add(a, b) { + var r, i, n; + n = a.length; + if (n != b.length) + throw TypeError("incompatible array size"); + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] + b[i]; + return r; + } + function array_sub(a, b) { + var r, i, n; + n = a.length; + if (n != b.length) + throw TypeError("incompatible array size"); + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] - b[i]; + return r; + } + function array_scalar_mul(a, b) { + var r, i, n; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] * b; + return r; + } + function array_mul(a, b) { + var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat; + h = a.length; + a_mat = Array.isArray(a[0]); + if (a_mat) { + l = a[0].length; + } else { + l = 1; + } + if (l != b.length) + throw RangeError("incompatible matrix size"); + b_mat = Array.isArray(b[0]); + if (b_mat) + w = b[0].length; + else + w = 1; + r = []; + if (a_mat && b_mat) { + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) { sum = 0; for(k = 0; k < l; k++) { - sum += a[i][k] * b[k]; + sum += a[i][k] * b[k][j]; } - r[i] = sum; + rl[j] = sum; } - } else if (!a_mat && b_mat) { - for(i = 0; i < h; i++) { - rl = []; - for(j = 0; j < w; j++) { - rl[j] = a[i] * b[0][j]; - } - r[i] = rl; + r[i] = rl; + } + } else if (a_mat && !b_mat) { + for(i = 0; i < h; i++) { + sum = 0; + for(k = 0; k < l; k++) { + sum += a[i][k] * b[k]; } - } else { - for(i = 0; i < h; i++) { - r[i] = a[i] * b[0]; + r[i] = sum; + } + } else if (!a_mat && b_mat) { + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) { + rl[j] = a[i] * b[0][j]; } + r[i] = rl; } - return r; - }, - [Symbol.operatorDiv](a, b) { - return Array[Symbol.operatorMul](a, b.inverse()); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - var n, i; - n = a.length; - if (n != b.length) + } else { + for(i = 0; i < h; i++) { + r[i] = a[i] * b[0]; + } + } + return r; + } + function array_div(a, b) { + return array_mul(a, b.inverse()); + } + function array_scalar_div(a, b) { + return a * b.inverse(); + } + function array_eq(a, b) { + var n, i; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) return false; - for(i = 0; i < n; i++) { - if (a[i] != b[i]) - return false; + } + return true; + } + + operators_set(Array.prototype, + { + "+": array_add, + "-": array_sub, + "*": array_mul, + "/": array_div, + "==": array_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var i, n, r; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = -a[i]; + return r; } - return true; }, - }); + { + right: [Number, BigInt, Float, Fraction, Complex, Mod, + Polynomial, PolyMod, RationalFunction, Series], + "*": array_scalar_mul, + "/": array_scalar_div, + "**": generic_pow, /* XXX: only for integer */ + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, + Polynomial, PolyMod, RationalFunction, Series], + "*"(a, b) { return array_scalar_mul(b, a); }, + "/"(a, b) { return array_scalar_div(b, a); }, + }); add_props(Array.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - var i, n, r; - n = this.length; - r = []; - for(i = 0; i < n; i++) - r[i] = -this[i]; - return r; - }, conj() { var i, n, r; n = this.length; @@ -2426,6 +2589,42 @@ function fromdeg(a) return a * Float.PI / 180; } +function sinh(a) +{ + var e = Float.exp(Float(a)); + return (e - 1/e) * 0.5; +} + +function cosh(a) +{ + var e = Float.exp(Float(a)); + return (e + 1/e) * 0.5; +} + +function tanh(a) +{ + var e = Float.exp(Float(a) * 2); + return (e - 1) / (e + 1); +} + +function asinh(a) +{ + var x = Float(a); + return log(sqrt(x * x + 1) + x); +} + +function acosh(a) +{ + var x = Float(a); + return log(sqrt(x * x - 1) + x); +} + +function atanh(a) +{ + var x = Float(a); + return 0.5 * log((1 + x) / (1 - x)); +} + var idn = Matrix.idn; var diag = Matrix.diag; var trans = Matrix.trans; diff --git a/quickjs-atom.h b/quickjs-atom.h index 92042d2..501aa1f 100644 --- a/quickjs-atom.h +++ b/quickjs-atom.h @@ -219,6 +219,8 @@ DEF(BigInt, "BigInt") DEF(BigFloat, "BigFloat") DEF(BigFloatEnv, "BigFloatEnv") DEF(BigDecimal, "BigDecimal") +DEF(OperatorSet, "OperatorSet") +DEF(Operators, "Operators") #endif DEF(Map, "Map") DEF(Set, "Set") /* Map + 1 */ @@ -263,27 +265,7 @@ DEF(Symbol_species, "Symbol.species") DEF(Symbol_unscopables, "Symbol.unscopables") DEF(Symbol_asyncIterator, "Symbol.asyncIterator") #ifdef CONFIG_BIGNUM -DEF(Symbol_operatorOrder, "Symbol.operatorOrder") -DEF(Symbol_operatorAdd, "Symbol.operatorAdd") -DEF(Symbol_operatorSub, "Symbol.operatorSub") -DEF(Symbol_operatorMul, "Symbol.operatorMul") -DEF(Symbol_operatorDiv, "Symbol.operatorDiv") -DEF(Symbol_operatorMod, "Symbol.operatorMod") -DEF(Symbol_operatorPow, "Symbol.operatorPow") -DEF(Symbol_operatorShl, "Symbol.operatorShl") -DEF(Symbol_operatorShr, "Symbol.operatorShr") -DEF(Symbol_operatorAnd, "Symbol.operatorAnd") -DEF(Symbol_operatorOr, "Symbol.operatorOr") -DEF(Symbol_operatorXor, "Symbol.operatorXor") -DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT") -DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE") -DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ") -DEF(Symbol_operatorPlus, "Symbol.operatorPlus") -DEF(Symbol_operatorNeg, "Symbol.operatorNeg") -DEF(Symbol_operatorNot, "Symbol.operatorNot") -DEF(Symbol_operatorInc, "Symbol.operatorInc") -DEF(Symbol_operatorDec, "Symbol.operatorDec") -DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod") +DEF(Symbol_operatorSet, "Symbol.operatorSet") #endif - + #endif /* DEF */ diff --git a/quickjs-libc.c b/quickjs-libc.c index da53fef..aff1b89 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2027,6 +2027,20 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, #if !defined(_WIN32) +static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + err = chdir(target); + JS_FreeCString(ctx, target); + return js_os_return(ctx, err); +} + /* return [path, errorcode] */ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -2581,6 +2595,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), #if !defined(_WIN32) + JS_CFUNC_DEF("chdir", 0, js_os_chdir ), JS_CFUNC_DEF("realpath", 1, js_os_realpath ), JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), diff --git a/quickjs-opcode.h b/quickjs-opcode.h index cc514e5..57f6969 100644 --- a/quickjs-opcode.h +++ b/quickjs-opcode.h @@ -260,9 +260,7 @@ DEF( or, 1, 2, 1, none) DEF(is_undefined_or_null, 1, 1, 1, none) #ifdef CONFIG_BIGNUM DEF( mul_pow10, 1, 2, 1, none) -DEF( math_div, 1, 2, 1, none) DEF( math_mod, 1, 2, 1, none) -DEF( math_pow, 1, 2, 1, none) #endif /* must be the last non short and non temporary opcode */ DEF( nop, 1, 0, 0, none) @@ -271,7 +269,6 @@ DEF( nop, 1, 0, 0, none) def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ -def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */ def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ @@ -42,10 +42,6 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" - -/* enable bignums */ -#define CONFIG_BIGNUM - #ifdef CONFIG_BIGNUM #include "libbf.h" #endif @@ -151,7 +147,8 @@ enum { JS_CLASS_BIG_INT, /* u.object_data */ JS_CLASS_BIG_FLOAT, /* u.object_data */ JS_CLASS_FLOAT_ENV, /* u.float_env */ - JS_CLASS_BIG_DECIMAL, /* u.object_data */ + JS_CLASS_BIG_DECIMAL, /* u.object_data */ + JS_CLASS_OPERATOR_SET, /* u.operator_set */ #endif JS_CLASS_MAP, /* u.map_state */ JS_CLASS_SET, /* u.map_state */ @@ -285,6 +282,7 @@ struct JSRuntime { JSNumericOperations bigint_ops; JSNumericOperations bigfloat_ops; JSNumericOperations bigdecimal_ops; + uint32_t operator_count; #endif }; @@ -300,8 +298,7 @@ struct JSClass { #define JS_MODE_STRICT (1 << 0) #define JS_MODE_STRIP (1 << 1) -#define JS_MODE_BIGINT (1 << 2) -#define JS_MODE_MATH (1 << 3) +#define JS_MODE_MATH (1 << 2) typedef struct JSStackFrame { struct JSStackFrame *prev_frame; /* NULL if first stack frame */ @@ -312,7 +309,7 @@ typedef struct JSStackFrame { const uint8_t *cur_pc; /* only used in bytecode functions : PC of the instruction after the call */ int arg_count; - int js_mode; /* 0 or JS_MODE_BIGINT for C functions */ + int js_mode; /* 0 or JS_MODE_MATH for C functions */ /* only used in generators. Current stack pointer value. NULL if the function is running. */ JSValue *cur_sp; @@ -419,8 +416,8 @@ struct JSContext { #ifdef CONFIG_BIGNUM bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ JSFloatEnv fp_env; /* global FP environment */ - BOOL bignum_ext; /* enable bigint mode, math mode and operator - overloading */ + BOOL bignum_ext : 8; /* enable math mode */ + BOOL allow_operator_overloading : 8; #endif /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; @@ -653,6 +650,53 @@ typedef struct JSAsyncFunctionData { JSAsyncFunctionState func_state; } JSAsyncFunctionData; +typedef enum { + /* binary operators */ + JS_OVOP_ADD, + JS_OVOP_SUB, + JS_OVOP_MUL, + JS_OVOP_DIV, + JS_OVOP_MOD, + JS_OVOP_POW, + JS_OVOP_OR, + JS_OVOP_AND, + JS_OVOP_XOR, + JS_OVOP_SHL, + JS_OVOP_SAR, + JS_OVOP_SHR, + JS_OVOP_EQ, + JS_OVOP_LESS, + + JS_OVOP_BINARY_COUNT, + /* unary operators */ + JS_OVOP_POS = JS_OVOP_BINARY_COUNT, + JS_OVOP_NEG, + JS_OVOP_INC, + JS_OVOP_DEC, + JS_OVOP_NOT, + + JS_OVOP_COUNT, +} JSOverloadableOperatorEnum; + +typedef struct { + uint32_t operator_index; + JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ +} JSBinaryOperatorDefEntry; + +typedef struct { + int count; + JSBinaryOperatorDefEntry *tab; +} JSBinaryOperatorDef; + +typedef struct { + uint32_t operator_counter; + BOOL is_primitive; /* OperatorSet for a primitive type */ + /* NULL if no operator is defined */ + JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ + JSBinaryOperatorDef left; + JSBinaryOperatorDef right; +} JSOperatorSetData; + typedef struct JSReqModuleEntry { JSAtom module_name; JSModuleDef *module; /* used using resolution */ @@ -808,6 +852,7 @@ struct JSObject { struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ #ifdef CONFIG_BIGNUM struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ + struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ #endif struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ @@ -991,6 +1036,11 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val, static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); +#ifdef CONFIG_BIGNUM +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +#endif static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); static int JS_ToBoolFree(JSContext *ctx, JSValue val); static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); @@ -1021,10 +1071,27 @@ static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); #ifdef CONFIG_BIGNUM static void js_float_env_finalizer(JSRuntime *rt, JSValue val); -static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a); -static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a); -static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint); -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a); +static JSValue JS_NewBigFloat(JSContext *ctx); +static inline bf_t *JS_GetBigFloat(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigDecimal(JSContext *ctx); +static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) +{ + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer); +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); @@ -1123,19 +1190,6 @@ static const JSClassExoticMethods js_proxy_exotic_methods; static const JSClassExoticMethods js_module_ns_exotic_methods; static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; -JS_BOOL JS_IsNumber(JSValueConst v) -{ -#ifdef CONFIG_BIGNUM - /* XXX: check the bignum case. Should use a different function */ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_BIG_FLOAT || JS_TAG_IS_FLOAT64(tag); -#else - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); -#endif -} - static void js_trigger_gc(JSRuntime *rt, size_t size) { BOOL force_gc; @@ -1333,6 +1387,7 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ + { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ #endif { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ @@ -2136,10 +2191,10 @@ static inline BOOL is_strict_mode(JSContext *ctx) } #ifdef CONFIG_BIGNUM -static inline BOOL is_bigint_mode(JSContext *ctx) +static inline BOOL is_math_mode(JSContext *ctx) { JSStackFrame *sf = ctx->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_BIGINT)); + return (sf && (sf->js_mode & JS_MODE_MATH)); } #endif @@ -2148,25 +2203,12 @@ JSValue JS_NewInt64(JSContext *ctx, int64_t v) if (v == (int32_t)v) { return JS_NewInt32(ctx, v); } else { -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, v); - return JS_NewBigInt(ctx, a); - } else -#endif - { - return __JS_NewFloat64(ctx, (double)v); - } + return __JS_NewFloat64(ctx, (double)v); } } static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { -#ifdef CONFIG_BIGNUM - return JS_NewInt64(ctx, val); -#else JSValue v; if (val <= 0x7fffffff) { v = JS_MKVAL(JS_TAG_INT, val); @@ -2174,7 +2216,6 @@ static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) v = __JS_NewFloat64(ctx, val); } return v; -#endif } /* JSAtom support */ @@ -5720,6 +5761,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) #ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: /* u.object_data */ case JS_CLASS_BIG_FLOAT: /* u.object_data */ + case JS_CLASS_BIG_DECIMAL: /* u.object_data */ #endif compute_value_size(p->u.object_data, hp); break; @@ -6515,28 +6557,17 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) case JS_TAG_BIG_INT: val = ctx->class_proto[JS_CLASS_BIG_INT]; break; - case JS_TAG_INT: - if (is_bigint_mode(ctx)) { - val = ctx->class_proto[JS_CLASS_BIG_INT]; - } else { - val = ctx->class_proto[JS_CLASS_NUMBER]; - } - break; - case JS_TAG_FLOAT64: - val = ctx->class_proto[JS_CLASS_NUMBER]; - break; case JS_TAG_BIG_FLOAT: val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; break; case JS_TAG_BIG_DECIMAL: val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; break; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: val = ctx->class_proto[JS_CLASS_NUMBER]; break; -#endif case JS_TAG_BOOL: val = ctx->class_proto[JS_CLASS_BOOLEAN]; break; @@ -7750,11 +7781,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop, } p->u.array.count = len; } -#ifdef CONFIG_BIGNUM - set_value(ctx, &prop->u.value, JS_NewUint32(ctx, len)); -#else prop->u.value = JS_NewUint32(ctx, len); -#endif } else { /* Note: length is always a uint32 because the object is an array */ @@ -9352,9 +9379,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) #define HINT_STRING 0 #define HINT_NUMBER 1 #define HINT_NONE 2 -#ifdef CONFIG_BIGNUM -#define HINT_INTEGER 3 -#endif /* don't try Symbol.toPrimitive */ #define HINT_FORCE_ORDINARY (1 << 4) @@ -9389,11 +9413,6 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) case HINT_NONE: atom = JS_ATOM_default; break; -#ifdef CONFIG_BIGNUM - case HINT_INTEGER: - atom = JS_ATOM_integer; - break; -#endif } arg = JS_AtomToString(ctx, atom); ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); @@ -9605,22 +9624,31 @@ static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, { bf_t a_s, *a = &a_s; int ret; - bf_init(ctx->bf_ctx, a); + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); if (ret & BF_ST_MEM_ERROR) { - bf_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigInt2(ctx, a, !(flags & ATOD_MODE_BIGINT)); + val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); + return val; } static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, int radix, int flags, slimb_t *pexponent) { - bf_t a_s, *a = &a_s; + bf_t *a; int ret; - - bf_init(ctx->bf_ctx, a); + JSValue val; + + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigFloat(val); if (flags & ATOD_ACCEPT_SUFFIX) { /* return the exponent to get infinite precision */ ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, @@ -9630,25 +9658,30 @@ static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, ctx->fp_env.flags); } if (ret & BF_ST_MEM_ERROR) { - bf_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigFloat(ctx, a); + return val; } static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, int radix, int flags, slimb_t *pexponent) { - bfdec_t a_s, *a = &a_s; + bfdec_t *a; int ret; - bfdec_init(ctx->bf_ctx, a); + JSValue val; + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigDecimal(val); ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, BF_RNDZ | BF_ATOF_NO_NAN_INF); if (ret & BF_ST_MEM_ERROR) { - bfdec_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigDecimal(ctx, a); + return val; } #endif @@ -9730,10 +9763,12 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, strstart(p, "Infinity", &p)) { #ifdef CONFIG_BIGNUM if (atod_type == ATOD_TYPE_BIG_FLOAT) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); + bf_t *a; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + goto done; + a = JS_GetBigFloat(val); bf_set_inf(a, is_neg); - val = JS_NewBigFloat(ctx, a); } else #endif { @@ -9810,7 +9845,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } else if (*p == 'l') { p++; atod_type = ATOD_TYPE_BIG_FLOAT; - } else if (*p == 'd') { + } else if (*p == 'm') { p++; atod_type = ATOD_TYPE_BIG_DECIMAL; } else { @@ -9901,7 +9936,6 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, typedef enum JSToNumberHintEnum { TON_FLAG_NUMBER, - TON_FLAG_INTEGER, TON_FLAG_NUMERIC, } JSToNumberHintEnum; @@ -9910,7 +9944,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, { uint32_t tag; JSValue ret; - int hint; redo: tag = JS_VALUE_GET_NORM_TAG(val); @@ -9924,12 +9957,19 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = val; break; case JS_TAG_BIG_INT: - if (flag == TON_FLAG_NUMBER && !is_bigint_mode(ctx)) { + if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); } - /* fall thru */ + ret = val; + break; case JS_TAG_BIG_FLOAT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); + } + ret = val; + break; #endif case JS_TAG_FLOAT64: case JS_TAG_INT: @@ -9944,12 +9984,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = JS_NAN; break; case JS_TAG_OBJECT: -#ifdef CONFIG_BIGNUM - hint = flag == TON_FLAG_INTEGER ? HINT_INTEGER : HINT_NUMBER; -#else - hint = HINT_NUMBER; -#endif - val = JS_ToPrimitiveFree(ctx, val, hint); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return JS_EXCEPTION; goto redo; @@ -9969,10 +10004,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = JS_NewInt32(ctx, 0); } else { int flags = ATOD_ACCEPT_BIN_OCT; -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) - flags |= ATOD_MODE_BIGINT; -#endif ret = js_atof(ctx, p, &p, 0, flags); if (!JS_IsException(ret)) { p += skip_spaces(p); @@ -10001,7 +10032,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); } -#ifdef CONFIG_BIGNUM static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) { return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); @@ -10011,7 +10041,6 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) { return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); } -#endif static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) @@ -10078,6 +10107,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); } +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { uint32_t tag; @@ -10106,11 +10136,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) } break; #ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - if (!is_bigint_mode(ctx)) - goto to_number; - ret = val; - break; case JS_TAG_BIG_FLOAT: { bf_t a_s, *a, r_s, *r = &r_s; @@ -10124,10 +10149,13 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) else ret = JS_DupValue(ctx, val); } else { - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_rint(r, BF_RNDZ); - ret = JS_NewBigInt(ctx, r); + ret = JS_NewBigInt(ctx); + if (!JS_IsException(ret)) { + r = JS_GetBigInt(ret); + bf_set(r, a); + bf_rint(r, BF_RNDZ); + ret = JS_CompactBigInt(ctx, ret); + } } if (a == &a_s) bf_delete(a); @@ -10136,9 +10164,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) break; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) return val; @@ -10182,17 +10207,12 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int32(&ret, &p->num, 0); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10261,17 +10281,12 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) return 0; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int64(pres, &p->num, 0); JS_FreeValue(ctx, val); } return 0; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10346,17 +10361,12 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10418,17 +10428,12 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10486,15 +10491,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } break; #ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - if (!is_bigint_mode(ctx)) - goto to_number; - bf_get_int32(&res, &p->num, 0); - JS_FreeValue(ctx, val); - } - goto int_clamp; case JS_TAG_BIG_FLOAT: { JSBigFloat *p = JS_VALUE_GET_PTR(val); @@ -10509,9 +10505,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) goto int_clamp; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { *pres = 0; @@ -10584,6 +10577,12 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) { int64_t v; @@ -10609,7 +10608,6 @@ static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, } /* Note: can return an exception */ -/* XXX: bignum case */ static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) { double d; @@ -10753,7 +10751,8 @@ static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); } -static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, + limb_t prec, int flags) { JSValue ret; bfdec_t *a; @@ -10764,8 +10763,7 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) saved_sign = a->sign; if (a->expn == BF_EXP_ZERO) a->sign = 0; - str = bfdec_ftoa(NULL, a, 0, BF_RNDZ | BF_FTOA_FORMAT_FREE | - BF_FTOA_JS_QUIRKS); + str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); a->sign = saved_sign; if (!str) return JS_ThrowOutOfMemory(ctx); @@ -10774,6 +10772,12 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) return ret; } +static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDZ | BF_FTOA_FORMAT_FREE); +} + #endif /* CONFIG_BIGNUM */ /* 2 <= base <= 36 */ @@ -11478,30 +11482,48 @@ static double js_pow(double a, double b) #ifdef CONFIG_BIGNUM +JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_si(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - BOOL is_bignum = is_bigint_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + if (is_math_mode(ctx) && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + return JS_NewInt64(ctx, v); } else { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, v); - return JS_NewBigInt2(ctx, a, TRUE); + return JS_NewBigInt64_1(ctx, v); } } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - BOOL is_bignum = is_bigint_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + JSValue val; + if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { + val = JS_NewInt64(ctx, v); } else { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_ui(a, v); - return JS_NewBigInt2(ctx, a, TRUE); + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } } + return val; } /* if the returned bigfloat is allocated it is equal to @@ -11582,13 +11604,10 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) p = str; p += skip_spaces(p); if ((p - str) == len) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, 0); - val = JS_NewBigInt(ctx, a); + val = JS_NewBigInt64(ctx, 0); } else { flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; - if (is_bigint_mode(ctx)) + if (is_math_mode(ctx)) flags |= ATOD_MODE_BIGINT; val = js_atof(ctx, p, &p, 0, flags); p += skip_spaces(p); @@ -11625,7 +11644,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; /* fall tru */ case JS_TAG_BOOL: @@ -11636,7 +11655,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_FLOAT64: { double d = JS_VALUE_GET_FLOAT64(val); - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; if (!isfinite(d)) goto fail; @@ -11651,7 +11670,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) r = &p->num; break; case JS_TAG_BIG_FLOAT: - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; p = JS_VALUE_GET_PTR(val); if (!bf_is_finite(&p->num)) @@ -11668,8 +11687,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) return NULL; goto redo; case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, - is_bigint_mode(ctx) ? HINT_INTEGER : HINT_NUMBER); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return NULL; goto redo; @@ -11692,21 +11710,26 @@ static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { return val; } else { - bf_t a_s, *a, b_s, *b; + bf_t a_s, *a, *r; int ret; - + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) + if (!a) { + JS_FreeValue(ctx, res); return JS_EXCEPTION; - b = &b_s; - bf_init(ctx->bf_ctx, b); - ret = bf_set(b, a); + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); JS_FreeBigInt(ctx, a, &a_s); if (ret) { - bf_delete(b); + JS_FreeValue(ctx, res); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigInt2(ctx, b, TRUE); + return JS_CompactBigInt(ctx, res); } } @@ -11745,7 +11768,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) static JSBigFloat *js_new_bf(JSContext *ctx) { JSBigFloat *p; - p = js_mallocz(ctx, sizeof(*p)); + p = js_malloc(ctx, sizeof(*p)); if (!p) return NULL; p->header.ref_count = 1; @@ -11753,224 +11776,387 @@ static JSBigFloat *js_new_bf(JSContext *ctx) return p; } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a) +static JSValue JS_NewBigFloat(JSContext *ctx) { - JSValue ret; JSBigFloat *p; - - p = js_new_bf(ctx); - bf_memcpy(&p->num, a); - ret = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - return ret; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_FLOAT, p); } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a) +static JSValue JS_NewBigDecimal(JSContext *ctx) { - JSValue ret; JSBigDecimal *p; - - p = js_mallocz(ctx, sizeof(*p)); + p = js_malloc(ctx, sizeof(*p)); if (!p) return JS_EXCEPTION; p->header.ref_count = 1; bfdec_init(ctx->bf_ctx, &p->num); - bfdec_memcpy(&p->num, a); - ret = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); - return ret; + return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint) +static JSValue JS_NewBigInt(JSContext *ctx) { - JSValue ret; JSBigFloat *p; - int32_t v; - - if (!force_bigint && bf_get_int32(&v, a, 0) == 0) { - /* can fit in an int32 */ - ret = JS_NewInt32(ctx, v); - bf_delete(a); - } else { - p = js_new_bf(ctx); - bf_memcpy(&p->num, a); - /* normalize the zero representation */ - if (bf_is_zero(&p->num)) - p->num.sign = 0; - ret = JS_MKPTR(JS_TAG_BIG_INT, p); - } - return ret; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_INT, p); } -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a) +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer) { - return JS_NewBigInt2(ctx, a, !is_bigint_mode(ctx)); + int64_t v; + bf_t *a; + + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + a = JS_GetBigInt(val); + if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + JS_FreeValue(ctx, val); + return JS_NewInt64(ctx, v); + } else if (a->expn == BF_EXP_ZERO && a->sign) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + assert(p->header.ref_count == 1); + a->sign = 0; + } + return val; } -/* return < 0 if exception, 0 if overloading method, 1 if overloading - operator called */ -static __exception int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op) -{ - JSAtom op_name; - JSValue method, ret, c1, c2; - BOOL bool_result, swap_op; - JSValueConst args[2]; +/* Convert the big int to a safe integer if in math mode. normalize + the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) +{ + return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); +} + +/* must be kept in sync with JSOverloadableOperatorEnum */ +static const char *js_overloadable_operator_names[JS_OVOP_COUNT] = { + "+", + "-", + "*", + "/", + "%", + "**", + "|", + "&", + "^", + "<<", + ">>", + ">>>", + "==", + "<", + "pos", + "neg", + "++", + "--", + "~", +}; - bool_result = FALSE; - swap_op = FALSE; - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; +static int get_ovop_from_opcode(OPCodeEnum op) +{ switch(op) { case OP_add: - op_name = JS_ATOM_Symbol_operatorAdd; - break; + return JS_OVOP_ADD; case OP_sub: - op_name = JS_ATOM_Symbol_operatorSub; - break; + return JS_OVOP_SUB; case OP_mul: - op_name = JS_ATOM_Symbol_operatorMul; - break; + return JS_OVOP_MUL; case OP_div: - case OP_math_div: - op_name = JS_ATOM_Symbol_operatorDiv; - break; + return JS_OVOP_DIV; case OP_mod: - op_name = JS_ATOM_Symbol_operatorMod; - break; - case OP_pow: - case OP_math_pow: - op_name = JS_ATOM_Symbol_operatorPow; - break; case OP_math_mod: - op_name = JS_ATOM_Symbol_operatorMathMod; - break; - case OP_shl: - op_name = JS_ATOM_Symbol_operatorShl; - break; - case OP_sar: - op_name = JS_ATOM_Symbol_operatorShr; - break; - case OP_and: - op_name = JS_ATOM_Symbol_operatorAnd; - break; + return JS_OVOP_MOD; + case OP_pow: + return JS_OVOP_POW; case OP_or: - op_name = JS_ATOM_Symbol_operatorOr; - break; + return JS_OVOP_OR; + case OP_and: + return JS_OVOP_AND; case OP_xor: - op_name = JS_ATOM_Symbol_operatorXor; - break; + return JS_OVOP_XOR; + case OP_shl: + return JS_OVOP_SHL; + case OP_sar: + return JS_OVOP_SAR; + case OP_shr: + return JS_OVOP_SHR; + case OP_eq: + case OP_neq: + return JS_OVOP_EQ; case OP_lt: - op_name = JS_ATOM_Symbol_operatorCmpLT; - bool_result = TRUE; - break; case OP_lte: - op_name = JS_ATOM_Symbol_operatorCmpLE; - bool_result = TRUE; - break; case OP_gt: - op_name = JS_ATOM_Symbol_operatorCmpLT; - bool_result = TRUE; - swap_op = TRUE; - break; case OP_gte: - op_name = JS_ATOM_Symbol_operatorCmpLE; - bool_result = TRUE; - swap_op = TRUE; - break; - case OP_eq: - case OP_neq: - op_name = JS_ATOM_Symbol_operatorCmpEQ; - bool_result = TRUE; - break; + return JS_OVOP_LESS; + case OP_plus: + return JS_OVOP_POS; + case OP_neg: + return JS_OVOP_NEG; + case OP_inc: + return JS_OVOP_INC; + case OP_dec: + return JS_OVOP_DEC; default: - goto fail; + abort(); } - c1 = JS_GetProperty(ctx, op1, JS_ATOM_constructor); - if (JS_IsException(c1)) +} + +/* return NULL if not present */ +static JSObject *find_binary_op(JSBinaryOperatorDef *def, + uint32_t operator_index, + JSOverloadableOperatorEnum op) +{ + JSBinaryOperatorDefEntry *ent; + int i; + for(i = 0; i < def->count; i++) { + ent = &def->tab[i]; + if (ent->operator_index == operator_index) + return ent->ops[op]; + } + return NULL; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op, + BOOL is_numeric, + int hint) +{ + JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1, *opset2; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + if (!ctx->allow_operator_overloading) + return 0; + + opset2_obj = JS_UNDEFINED; + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) goto exception; - c2 = JS_GetProperty(ctx, op2, JS_ATOM_constructor); - if (JS_IsException(c2)) + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) goto exception; - if (JS_VALUE_GET_TAG(c1) != JS_TAG_OBJECT || - JS_VALUE_GET_TAG(c2) != JS_TAG_OBJECT) - goto fail; - if (JS_VALUE_GET_OBJ(c1) == JS_VALUE_GET_OBJ(c2)) { - /* if same constructor, there is no ambiguity */ - method = JS_GetProperty(ctx, c1, op_name); + + opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset2_obj)) + goto exception; + if (JS_IsUndefined(opset2_obj)) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); + if (!opset2) + goto exception; + + if (opset1->is_primitive && opset2->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + if (opset1->operator_counter == opset2->operator_counter) { + p = opset1->self_ops[ovop]; + } else if (opset1->operator_counter > opset2->operator_counter) { + p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); } else { - JSValue val; - int order1, order2; + p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); + } + if (!p) { + JS_ThrowTypeError(ctx, "operator %s: no function defined", + js_overloadable_operator_names[ovop]); + goto exception; + } - /* different constructors: we use a user-defined ordering */ - val = JS_GetProperty(ctx, c1, JS_ATOM_Symbol_operatorOrder); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) - goto undef_order; - if (JS_ToInt32Free(ctx, &order1, val)) - goto exception; - val = JS_GetProperty(ctx, c2, JS_ATOM_Symbol_operatorOrder); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) { - undef_order: - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); - *pret = JS_UNDEFINED; - return 0; + if (opset1->is_primitive) { + if (is_numeric) { + new_op1 = JS_ToNumeric(ctx, op1); + } else { + new_op1 = JS_ToPrimitive(ctx, op1, hint); } - if (JS_ToInt32Free(ctx, &order2, val)) - goto exception; - /* ambiguous priority: error */ - if (order1 == order2) { - JS_ThrowTypeError(ctx, "operator_order is identical in both constructors"); + if (JS_IsException(new_op1)) goto exception; - } - if (order1 > order2) { - val = c1; + } else { + new_op1 = JS_DupValue(ctx, op1); + } + + if (opset2->is_primitive) { + if (is_numeric) { + new_op2 = JS_ToNumeric(ctx, op2); } else { - val = c2; + new_op2 = JS_ToPrimitive(ctx, op2, hint); } - method = JS_GetProperty(ctx, val, op_name); - } - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - *pret = JS_UNDEFINED; - return 0; + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + } else { + new_op2 = JS_DupValue(ctx, op2); } - if (swap_op) { - args[0] = op2; - args[1] = op1; + + /* XXX: could apply JS_ToPrimitive() if primitive type so that the + operator function does not get a value object */ + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { + args[0] = new_op2; + args[1] = new_op1; } else { - args[0] = op1; - args[1] = op2; + args[0] = new_op1; + args[1] = new_op2; } ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); if (JS_IsException(ret)) goto exception; - if (bool_result) { + if (ovop == JS_OVOP_EQ) { BOOL res = JS_ToBoolFree(ctx, ret); if (op == OP_neq) res ^= 1; ret = JS_NewBool(ctx, res); + } else if (ovop == JS_OVOP_LESS) { + if (JS_IsUndefined(ret)) { + ret = JS_FALSE; + } else { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_lte || op == OP_gte) + res ^= 1; + ret = JS_NewBool(ctx, res); + } } + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); *pret = ret; return 1; - fail: - JS_ThrowTypeError(ctx, "invalid types for binary operator"); exception: - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* try to call the operation on the operatorSet field of 'obj'. Only + used for "/" and "**" on the BigInt prototype in math mode */ +static __exception int js_call_binary_op_simple(JSContext *ctx, + JSValue *pret, + JSValueConst obj, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + new_op1 = JS_ToNumeric(ctx, op1); + if (JS_IsException(new_op1)) + goto exception; + new_op2 = JS_ToNumeric(ctx, op2); + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + args[0] = new_op1; + args[1] = new_op2; + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_unary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + + if (!ctx->allow_operator_overloading) + return 0; + + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + if (opset1->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_ThrowTypeError(ctx, "no overloaded operator %s", + js_overloadable_operator_names[ovop]); + goto exception; + } + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); *pret = JS_UNDEFINED; return -1; } @@ -11993,16 +12179,22 @@ static JSValue throw_bf_exception(JSContext *ctx, int status) static int js_unary_arith_bigint(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bf_t a_s, r_s, *r = &r_s, *a; + bf_t a_s, *r, *a; int ret, v; + JSValue res; - if (op == OP_plus && !is_bigint_mode(ctx)) { + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigint argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigInt(res); a = JS_ToBigInt(ctx, &a_s, op1); - bf_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12027,28 +12219,35 @@ static int js_unary_arith_bigint(JSContext *ctx, JS_FreeBigInt(ctx, a, &a_s); JS_FreeValue(ctx, op1); if (unlikely(ret)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigInt(ctx, r); + res = JS_CompactBigInt(ctx, res); + *pres = res; return 0; } static int js_unary_arith_bigfloat(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bf_t a_s, r_s, *r = &r_s, *a; + bf_t a_s, *r, *a; int ret, v; + JSValue res; - if (op == OP_plus && !is_bigint_mode(ctx)) { + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigFloat(res); a = JS_ToBigFloat(ctx, &a_s, op1); - bf_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12070,28 +12269,34 @@ static int js_unary_arith_bigfloat(JSContext *ctx, bf_delete(a); JS_FreeValue(ctx, op1); if (unlikely(ret & BF_ST_MEM_ERROR)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigFloat(ctx, r); + *pres = res; return 0; } static int js_unary_arith_bigdecimal(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bfdec_t r_s, *r = &r_s, *a; + bfdec_t *r, *a; int ret, v; - - if (op == OP_plus && !is_bigint_mode(ctx)) { + JSValue res; + + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigDecimal(res); a = JS_ToBigDecimal(ctx, op1); - bfdec_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12111,11 +12316,11 @@ static int js_unary_arith_bigdecimal(JSContext *ctx, } JS_FreeValue(ctx, op1); if (unlikely(ret)) { - bfdec_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigDecimal(ctx, r); + *pres = res; return 0; } @@ -12123,112 +12328,93 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, val, method; - BOOL is_legacy; - JSAtom op_name; - int v; + JSValue op1, val; + int v, ret; uint32_t tag; op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; - if (JS_IsObject(op1) && ctx->bignum_ext) { - switch(op) { - case OP_plus: - op_name = JS_ATOM_Symbol_operatorPlus; - break; - case OP_neg: - op_name = JS_ATOM_Symbol_operatorNeg; - break; - case OP_inc: - op_name = JS_ATOM_Symbol_operatorInc; - break; - case OP_dec: - op_name = JS_ATOM_Symbol_operatorDec; - break; - default: - abort(); - } - method = JS_GetProperty(ctx, op1, op_name); - if (JS_IsException(method)) - return -1; - if (JS_IsUndefined(method) || JS_IsNull(method)) - goto to_number; - val = JS_CallFree(ctx, method, op1, 0, NULL); - if (JS_IsException(val)) + if (JS_IsObject(op1)) { + ret = js_call_unary_op_fallback(ctx, &val, op1, op); + if (ret < 0) return -1; - JS_FreeValue(ctx, op1); - sp[-1] = val; - } else { - to_number: - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) - goto exception; - is_legacy = is_bigint_mode(ctx) ^ 1; - tag = JS_VALUE_GET_TAG(op1); - switch(tag) { - case JS_TAG_INT: - { - int64_t v64; - v64 = JS_VALUE_GET_INT(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - v64 += v; - break; - case OP_plus: - break; - case OP_neg: - if (v64 == 0 && is_legacy) { - sp[-1] = __JS_NewFloat64(ctx, -0.0); - return 0; - } else { - v64 = -v64; - } - break; - default: - abort(); + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = __JS_NewFloat64(ctx, -0.0); + return 0; + } else { + v64 = -v64; } - sp[-1] = JS_NewInt64(ctx, v64); + break; + default: + abort(); } - break; - case JS_TAG_BIG_INT: - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - case JS_TAG_BIG_FLOAT: - if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - case JS_TAG_BIG_DECIMAL: - if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - default: - handle_float64: - { - double d; - d = JS_VALUE_GET_FLOAT64(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - d += v; - break; - case OP_plus: - break; - case OP_neg: - d = -d; - break; - default: - abort(); - } - sp[-1] = __JS_NewFloat64(ctx, d); + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_BIG_INT: + handle_bigint: + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_FLOAT: + if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_DECIMAL: + if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + default: + handle_float64: + { + double d; + if (is_math_mode(ctx)) + goto handle_bigint; + d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); } - break; + sp[-1] = __JS_NewFloat64(ctx, d); } + break; } return 0; exception: @@ -12255,39 +12441,33 @@ static __exception int js_post_inc_slow(JSContext *ctx, static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { - JSValue op1, method, val; - + JSValue op1, val; + int ret; + op1 = sp[-1]; if (JS_IsObject(op1)) { - if (!ctx->bignum_ext) - goto to_number; - method = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorNot); - if (JS_IsException(method)) - return -1; - if (JS_IsUndefined(method) || JS_IsNull(method)) - goto to_number; - val = JS_CallFree(ctx, method, op1, 0, NULL); - if (JS_IsException(val)) + ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); + if (ret < 0) return -1; - JS_FreeValue(ctx, op1); - sp[-1] = val; - } else { - if (JS_IsString(op1)) { - to_number: - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); - if (JS_IsException(op1)) - goto exception; - } - if (is_bigint_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) - goto exception; - } else { - int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) - goto exception; - sp[-1] = JS_NewInt32(ctx, ~v1); + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; } } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = JS_NewInt32(ctx, ~v1); + } return 0; exception: sp[-1] = JS_UNDEFINED; @@ -12297,12 +12477,19 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bf_t a_s, b_s, r_s, *r, *a, *b; - int ret, rnd_mode; + bf_t a_s, b_s, *r, *a, *b; + int ret; + JSValue res; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; + } + r = JS_GetBigFloat(res); a = JS_ToBigFloat(ctx, &a_s, op1); b = JS_ToBigFloat(ctx, &b_s, op2); - r = &r_s; bf_init(ctx->bf_ctx, r); switch(op) { case OP_add: @@ -12314,29 +12501,21 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); break; - case OP_math_div: case OP_div: ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_mod; + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_DIVREM_EUCLIDIAN); + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_mod: - { - bf_t q_s, *q = &q_s; - bf_init(ctx->bf_ctx, q); - ret = bf_divrem(q, r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - rnd_mode); - bf_delete(q); - } + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_RNDZ); break; case OP_pow: - case OP_math_pow: ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUICKS); + ctx->fp_env.flags | BF_POW_JS_QUIRKS); break; default: abort(); @@ -12348,21 +12527,24 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret & BF_ST_MEM_ERROR)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigFloat(ctx, r); + *pres = res; return 0; } static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bf_t a_s, b_s, r_s, *r, *a, *b; - int ret, rnd_mode; + bf_t a_s, b_s, *r, *a, *b; + int ret; JSValue res; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + goto fail; a = JS_ToBigInt(ctx, &a_s, op1); if (!a) goto fail; @@ -12371,8 +12553,7 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JS_FreeBigInt(ctx, a, &a_s); goto fail; } - r = &r_s; - bf_init(ctx->bf_ctx, r); + r = JS_GetBigInt(res); ret = 0; switch(op) { case OP_add: @@ -12384,73 +12565,75 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); break; - case OP_math_div: - goto op_fallback; case OP_div: - if (!is_bigint_mode(ctx)) { + if (!is_math_mode(ctx)) { bf_t rem_s, *rem = &rem_s; bf_init(ctx->bf_ctx, rem); ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); bf_delete(rem); } else { - bf_div(r, a, b, 53, bf_set_exp_bits(11) | - BF_RNDN | BF_FLAG_SUBNORMAL); - goto float64_result; + goto math_mode_div_pow; } break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_int_mod; + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP; + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_int_mod: - { - bf_t q_s, *q = &q_s; - bf_init(ctx->bf_ctx, q); - ret = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, - rnd_mode) & BF_ST_INVALID_OP; - bf_delete(q); - } + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ) & BF_ST_INVALID_OP; break; case OP_pow: - case OP_math_pow: if (b->sign) { - if (!is_bigint_mode(ctx)) { + if (!is_math_mode(ctx)) { ret = BF_ST_INVALID_OP; - } else if (op == OP_math_pow) { - op_fallback: - bf_delete(r); + } else { + math_mode_div_pow: + JS_FreeValue(ctx, res); + ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op); + if (ret != 0) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + return -1; + } else { + *pres = res; + return 0; + } + } + /* if no BigInt power operator defined, return a + bigfloat */ + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + goto fail; + } + r = JS_GetBigFloat(res); + if (op == OP_div) { + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR; + } else { + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR; + } JS_FreeBigInt(ctx, a, &a_s); JS_FreeBigInt(ctx, b, &b_s); - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - if (ret < 0) { - return -1; - } else if (ret == 0) { - JS_ThrowTypeError(ctx, "operator must be defined for exact division or power"); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); return -1; } *pres = res; return 0; - } else { - double dr; - bf_pow(r, a, b, 53, bf_set_exp_bits(11) | - BF_RNDN | BF_FLAG_SUBNORMAL); - float64_result: - bf_get_float64(r, &dr, BF_RNDN); - bf_delete(r); - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - *pres = __JS_NewFloat64(ctx, dr); - return 0; } } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUICKS); + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); } break; @@ -12494,13 +12677,14 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigInt(ctx, r); + *pres = JS_CompactBigInt(ctx, res); return 0; fail: + JS_FreeValue(ctx, res); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); return -1; @@ -12536,18 +12720,21 @@ static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bfdec_t r_s, *r, *a, *b; - int ret, rnd_mode; - - /* big decimal result */ + bfdec_t *r, *a, *b; + int ret; + JSValue res; + + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) + goto fail; + r = JS_GetBigDecimal(res); + a = JS_ToBigDecimal(ctx, op1); if (!a) goto fail; b = JS_ToBigDecimal(ctx, op2); if (!b) goto fail; - r = &r_s; - bfdec_init(ctx->bf_ctx, r); switch(op) { case OP_add: ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ); @@ -12558,26 +12745,17 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ); break; - case OP_math_div: case OP_div: ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_mod_dec; + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN); + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_mod_dec: - { - bfdec_t q_s, *q = &q_s; - bfdec_init(ctx->bf_ctx, q); - ret = bfdec_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, rnd_mode); - bfdec_delete(q); - } + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); break; case OP_pow: - case OP_math_pow: ret = js_bfdec_pow(r, a, b); break; default: @@ -12586,13 +12764,14 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret)) { - bfdec_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigDecimal(ctx, r); + *pres = res; return 0; fail: + JS_FreeValue(ctx, res); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); return -1; @@ -12602,7 +12781,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s OPCodeEnum op) { JSValue op1, op2, res; - BOOL is_legacy; uint32_t tag1, tag2; int ret; double d1, d2; @@ -12619,12 +12797,11 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12655,23 +12832,23 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s int64_t v; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); - is_legacy = is_bigint_mode(ctx) ^ 1; switch(op) { case OP_sub: v = (int64_t)v1 - (int64_t)v2; break; case OP_mul: v = (int64_t)v1 * (int64_t)v2; - if (is_legacy && v == 0 && (v1 | v2) < 0) { + if (is_math_mode(ctx) && + (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) + goto handle_bigint; + if (v == 0 && (v1 | v2) < 0) { sp[-2] = __JS_NewFloat64(ctx, -0.0); return 0; } break; - case OP_math_div: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - return 0; case OP_div: + if (is_math_mode(ctx)) + goto handle_bigint; sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; case OP_math_mod: @@ -12688,26 +12865,19 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_mod: - if (is_legacy && (v1 < 0 || v2 <= 0)) { + if (v1 < 0 || v2 <= 0) { sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); return 0; } else { - if (unlikely(v2 == 0)) { - throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); - goto exception; - } v = (int64_t)v1 % (int64_t)v2; } break; case OP_pow: - case OP_math_pow: - if (is_legacy) { + if (!is_math_mode(ctx)) { sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); return 0; } else { - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - return 0; + goto handle_bigint; } break; default: @@ -12720,6 +12890,10 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) goto exception; + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; } else if (tag1 == JS_TAG_FLOAT64 || tag2 == JS_TAG_FLOAT64) { double dr; /* float64 result */ @@ -12730,6 +12904,8 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; handle_float64: + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; switch(op) { case OP_sub: dr = d1 - d2; @@ -12738,7 +12914,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s dr = d1 * d2; break; case OP_div: - case OP_math_div: dr = d1 / d2; break; case OP_mod: @@ -12752,16 +12927,12 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s dr += d2; break; case OP_pow: - case OP_math_pow: dr = js_pow(d1, d2); break; default: abort(); } sp[-2] = __JS_NewFloat64(ctx, dr); - } else { - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; } return 0; exception: @@ -12792,12 +12963,14 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && + tag2 != JS_TAG_STRING)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED && + tag1 != JS_TAG_STRING))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add, + FALSE, HINT_NONE); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12886,6 +13059,7 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, JSValue op1, op2, res; int ret; uint32_t tag1, tag2; + uint32_t v1, v2, r; op1 = sp[-2]; op2 = sp[-1]; @@ -12893,12 +13067,11 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag2 = JS_VALUE_GET_NORM_TAG(op2); /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12911,63 +13084,60 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, } } - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); + op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); goto exception; } - op2 = JS_ToNumberHintFree(ctx, op2, TON_FLAG_INTEGER); + op2 = JS_ToNumericFree(ctx, op2); if (JS_IsException(op2)) { JS_FreeValue(ctx, op1); goto exception; } - if (!is_bigint_mode(ctx)) { - uint32_t v1, v2, r; + if (is_math_mode(ctx)) + goto bigint_op; - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } else { - goto bigint_op; - } + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 != tag2) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowTypeError(ctx, "both operands must be bigint"); + goto exception; } else { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + bigint_op: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) goto exception; - switch(op) { - case OP_shl: - r = v1 << (v2 & 0x1f); - break; - case OP_sar: - r = (int)v1 >> (v2 & 0x1f); - break; - case OP_and: - r = v1 & v2; - break; - case OP_or: - r = v1 | v2; - break; - case OP_xor: - r = v1 ^ v2; - break; - default: - abort(); - } - sp[-2] = JS_NewInt32(ctx, r); } } else { - bigint_op: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = JS_NewInt32(ctx, r); } return 0; exception: @@ -13081,12 +13251,12 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, + FALSE, HINT_NUMBER); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13165,7 +13335,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } else { if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && - !is_bigint_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -13271,10 +13441,11 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, goto exception; } } else if (tag1 == tag2) { - if (tag1 == JS_TAG_OBJECT && ctx->bignum_ext) { + if (tag1 == JS_TAG_OBJECT) { /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13294,7 +13465,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_bigint_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -13336,7 +13507,8 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13391,7 +13563,7 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) goto exception; } /* XXX: could forbid >>> in bignum mode */ - if (!is_bigint_mode(ctx) && + if (!is_math_mode(ctx) && (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); @@ -13433,34 +13605,38 @@ static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) { - bf_t a_s, *a, r_s, *r = &r_s; - JSValue op1, op2; - slimb_t e; + bf_t a_s, *a, *r; + JSValue op1, op2, res; + int64_t e; int ret; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) + return -1; + r = JS_GetBigFloat(res); op1 = sp[-2]; op2 = sp[-1]; a = JS_ToBigFloat(ctx, &a_s, op1); if (!a) return -1; -#if LIMB_BITS == 32 - ret = JS_ToInt32(ctx, &e, op2); -#else - ret = JS_ToInt64(ctx, &e, op2); -#endif + if (JS_IsBigInt(ctx, op2)) { + ret = JS_ToBigInt64(ctx, &e, op2); + } else { + ret = JS_ToInt64(ctx, &e, op2); + } if (ret) { if (a == &a_s) bf_delete(a); + JS_FreeValue(ctx, res); return -1; } - bf_init(ctx->bf_ctx, r); bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); if (a == &a_s) bf_delete(a); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBigFloat(ctx, r); + sp[-2] = res; return 0; } @@ -13882,22 +14058,9 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, d2 = JS_VALUE_GET_INT(op2); goto number_test; } else if (tag2 == JS_TAG_FLOAT64) { -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) { - res = FALSE; - } else -#endif - { - d2 = JS_VALUE_GET_FLOAT64(op2); - goto number_test; - } - } else -#ifdef CONFIG_BIGNUM - if (tag2 == JS_TAG_BIG_INT && is_bigint_mode(ctx)) { - goto bigint_test; - } else -#endif - { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { res = FALSE; } break; @@ -13905,11 +14068,7 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, d1 = JS_VALUE_GET_FLOAT64(op1); if (tag2 == JS_TAG_FLOAT64) { d2 = JS_VALUE_GET_FLOAT64(op2); - } else if (tag2 == JS_TAG_INT -#ifdef CONFIG_BIGNUM - && !is_bigint_mode(ctx) -#endif - ) { + } else if (tag2 == JS_TAG_INT) { d2 = JS_VALUE_GET_INT(op2); } else { res = FALSE; @@ -13936,15 +14095,10 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, case JS_TAG_BIG_INT: { bf_t a_s, *a, b_s, *b; - if (tag1 == tag2) { - /* OK */ - } else if (tag2 == JS_TAG_INT && is_bigint_mode(ctx)) { - /* OK */ - } else { + if (tag1 != tag2) { res = FALSE; break; } - bigint_test: a = JS_ToBigFloat(ctx, &a_s, op1); b = JS_ToBigFloat(ctx, &b_s, op2); res = bf_cmp_eq(a, b); @@ -14100,30 +14254,20 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bigint_mode(ctx)) - atom = JS_ATOM_bigint; - else - atom = JS_ATOM_number; - break; case JS_TAG_BIG_INT: atom = JS_ATOM_bigint; break; - case JS_TAG_FLOAT64: - atom = JS_ATOM_number; - break; case JS_TAG_BIG_FLOAT: atom = JS_ATOM_bigfloat; break; case JS_TAG_BIG_DECIMAL: atom = JS_ATOM_bigdecimal; break; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: atom = JS_ATOM_number; break; -#endif case JS_TAG_UNDEFINED: atom = JS_ATOM_undefined; break; @@ -15288,7 +15432,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, /* we only propagate the bignum mode as some runtime functions test it */ if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_BIGINT; + sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; else sf->js_mode = 0; #else @@ -17315,11 +17459,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); r = (int64_t)v1 * v2; -#ifdef CONFIG_BIGNUM - if (unlikely((int)r != r) || (r == 0 && !is_bigint_mode(ctx))) - goto binary_arith_slow; -#else if (unlikely((int)r != r)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH) && + (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) + goto binary_arith_slow; +#endif d = (double)r; goto mul_fp_res; } @@ -17328,14 +17473,15 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, d = -0.0; goto mul_fp_res; } -#endif sp[-2] = JS_NewInt32(ctx, r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; +#endif d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); -#ifndef CONFIG_BIGNUM mul_fp_res: -#endif sp[-2] = __JS_NewFloat64(ctx, d); sp--; } else { @@ -17343,11 +17489,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#ifdef CONFIG_BIGNUM - CASE(OP_math_div): - CASE(OP_div): - goto binary_arith_slow; -#else CASE(OP_div): { JSValue op1, op2; @@ -17355,6 +17496,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, op2 = sp[-1]; if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { int v1, v2; + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); @@ -17364,7 +17507,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#endif CASE(OP_mod): #ifdef CONFIG_BIGNUM CASE(OP_math_mod): @@ -17390,9 +17532,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } BREAK; CASE(OP_pow): -#ifdef CONFIG_BIGNUM - CASE(OP_math_pow): -#endif binary_arith_slow: if (js_binary_arith_slow(ctx, sp, opcode)) goto exception; @@ -17422,11 +17561,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, tag = JS_VALUE_GET_TAG(op1); if (tag == JS_TAG_INT) { val = JS_VALUE_GET_INT(op1); -#ifdef CONFIG_BIGNUM - if (unlikely(val == INT32_MIN) || - (val == 0 && !is_bigint_mode(ctx))) - goto slow_neg; -#else /* Note: -0 cannot be expressed as integer */ if (unlikely(val == 0)) { d = -0.0; @@ -17436,18 +17570,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, d = -(double)val; goto neg_fp_res; } -#endif sp[-1] = JS_NewInt32(ctx, -val); } else if (JS_TAG_IS_FLOAT64(tag)) { d = -JS_VALUE_GET_FLOAT64(op1); -#ifndef CONFIG_BIGNUM neg_fp_res: -#endif sp[-1] = __JS_NewFloat64(ctx, d); } else { -#ifdef CONFIG_BIGNUM - slow_neg: -#endif if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17560,7 +17688,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, #ifdef CONFIG_BIGNUM { int64_t r; - if (is_bigint_mode(ctx)) { + if (unlikely(sf->js_mode & JS_MODE_MATH)) { if (v2 > 0x1f) goto shl_slow; r = (int64_t)v1 << v2; @@ -17616,7 +17744,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, v2 = JS_VALUE_GET_INT(op2); #ifdef CONFIG_BIGNUM if (unlikely(v2 > 0x1f)) { - if (is_bigint_mode(ctx)) + if (unlikely(sf->js_mode & JS_MODE_MATH)) goto sar_slow; else v2 &= 0x1f; @@ -20182,7 +20310,7 @@ static __exception int next_token(JSParseState *s) ATOD_ACCEPT_UNDERSCORES; #ifdef CONFIG_BIGNUM flags |= ATOD_ACCEPT_SUFFIX; - if (s->cur_func->js_mode & JS_MODE_BIGINT) { + if (s->cur_func->js_mode & JS_MODE_MATH) { flags |= ATOD_MODE_BIGINT; if (s->cur_func->js_mode & JS_MODE_MATH) flags |= ATOD_TYPE_BIG_FLOAT; @@ -23312,7 +23440,6 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else #ifdef CONFIG_BIGNUM if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - bf_t r_s, *r = &r_s; slimb_t e; int ret; @@ -23326,9 +23453,9 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_push_i32); emit_u32(s, e); } else { - bf_init(s->ctx->bf_ctx, r); - bf_set_si(r, e); - val = JS_NewBigInt(s->ctx, r); + val = JS_NewBigInt64_1(s->ctx, e); + if (JS_IsException(val)) + return -1; ret = emit_push_const(s, val, 0); JS_FreeValue(s->ctx, val); if (ret < 0) @@ -24105,10 +24232,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return -1; if (js_parse_unary(s, 1)) return -1; - if (s->cur_func->js_mode & JS_MODE_MATH) - emit_op(s, OP_math_pow); - else - emit_op(s, OP_pow); + emit_op(s, OP_pow); } #else if (s->token.val == TOK_POW) { @@ -24151,12 +24275,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_mul; break; case '/': -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) - opcode = OP_math_div; - else -#endif - opcode = OP_div; + opcode = OP_div; break; case '%': #ifdef CONFIG_BIGNUM @@ -24549,12 +24668,8 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) op = assign_opcodes[op - TOK_MUL_ASSIGN]; #ifdef CONFIG_BIGNUM if (s->cur_func->js_mode & JS_MODE_MATH) { - if (op == OP_div) - op = OP_math_div; - else if (op == OP_mod) + if (op == OP_mod) op = OP_math_mod; - else if (op == OP_pow) - op = OP_math_pow; } #endif emit_op(s, op); @@ -30149,20 +30264,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) len = opcode_info[op].size; pos_next = pos + len; switch(op) { - case OP_close_var_object: - { - if (s->var_object_idx >= 0) { - /* close the var object and create a new one */ - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_close_loc); - dbuf_put_u16(&bc_out, s->var_object_idx); - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->var_object_idx); - } - } - break; - case OP_line_num: /* line number info (for debug). We put it in a separate compressed table to reduce memory usage and get better @@ -31508,11 +31609,8 @@ static __exception int js_parse_directives(JSParseState *s) } #endif #ifdef CONFIG_BIGNUM - else if (s->ctx->bignum_ext && !strcmp(str, "use bigint")) { - s->cur_func->js_mode |= JS_MODE_BIGINT; - } else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { - s->cur_func->js_mode |= JS_MODE_BIGINT | JS_MODE_MATH; + s->cur_func->js_mode |= JS_MODE_MATH; } #endif } @@ -31804,13 +31902,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if (js_parse_destructing_element(s, TOK_VAR, 1, TRUE, -1, TRUE)) goto fail; - /* Close var object: necessary if direct eval call - occurred in the assignment expression or if any - variable was captured and a later direct eval call - may instantiate it in the var object. - The next pass will generate the capture if required. - */ - emit_op(s, OP_close_var_object); } else if (s->token.val == TOK_IDENT) { if (s->token.u.ident.is_reserved) { js_parse_error_reserved_identifier(s); @@ -31889,8 +31980,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, set_object_name(s, name); emit_op(s, OP_put_arg); emit_u16(s, idx); - /* Close var object: see above comment. */ - emit_op(s, OP_close_var_object); emit_label(s, label); } } else if (!has_opt_arg) { @@ -34399,30 +34488,20 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_EXCEPTION: return JS_DupValue(ctx, val); #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bigint_mode(ctx)) - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); - else - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; - case JS_TAG_FLOAT64: - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_FLOAT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); goto set_value; case JS_TAG_BIG_DECIMAL: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); goto set_value; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); goto set_value; -#endif case JS_TAG_STRING: /* XXX: should call the string constructor */ { @@ -34995,10 +35074,13 @@ static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val ret = JS_PreventExtensions(ctx, obj); if (ret < 0) return JS_EXCEPTION; - if (reflect) + if (reflect) { return JS_NewBool(ctx, ret); - else + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); return JS_DupValue(ctx, obj); + } } static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, @@ -35166,7 +35248,7 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, goto exception; } js_free_prop_enum(ctx, props, len); - return JS_TRUE; + return JS_DupValue(ctx, obj); exception: js_free_prop_enum(ctx, props, len); @@ -37690,17 +37772,14 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { JSValue val, obj; -#ifdef CONFIG_BIGNUM if (argc == 0) { - if (is_bigint_mode(ctx)) - val = __JS_NewFloat64(ctx, 0); - else - val = JS_NewInt32(ctx, 0); + val = JS_NewInt32(ctx, 0); } else { val = JS_ToNumeric(ctx, argv[0]); if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: case JS_TAG_BIG_FLOAT: { @@ -37711,25 +37790,19 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, val = __JS_NewFloat64(ctx, d); } break; - case JS_TAG_INT: - if (is_bigint_mode(ctx)) { - /* always return a number in bignum mode */ - val = __JS_NewFloat64(ctx, JS_VALUE_GET_INT(val)); - } + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + return val; + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; break; +#endif default: break; } } -#else - if (argc == 0) { - val = JS_NewInt32(ctx, 0); - } else { - val = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(val)) - return val; - } -#endif if (!JS_IsUndefined(new_target)) { obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); if (!JS_IsException(obj)) @@ -37760,7 +37833,6 @@ static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - /* XXX: should just check for float and big_float */ if (!JS_IsNumber(argv[0])) return JS_FALSE; return js_global_isNaN(ctx, this_val, argc, argv); @@ -37793,8 +37865,7 @@ static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, return JS_FALSE; if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; - return JS_NewBool(ctx, isfinite(d) && floor(d) == d && - fabs(d) <= (double)MAX_SAFE_INTEGER); + return JS_NewBool(ctx, is_safe_integer(d)); } static const JSCFunctionListEntry js_number_funcs[] = { @@ -37838,6 +37909,18 @@ static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisNumberValue(ctx, this_val); } +static int js_get_radix(JSContext *ctx, JSValueConst val) +{ + int radix; + if (JS_ToInt32Sat(ctx, &radix, val)) + return -1; + if (radix < 2 || radix > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + return -1; + } + return radix; +} + static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -37851,12 +37934,9 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, if (magic || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) - goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - } } if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; @@ -37972,10 +38052,6 @@ static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, p = str; p += skip_spaces(p); flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) - flags |= ATOD_MODE_BIGINT; -#endif ret = js_atof(ctx, p, NULL, radix, flags); } JS_FreeCString(ctx, str); @@ -39738,348 +39814,6 @@ static double js_fmax(double a, double b) } } -#ifdef CONFIG_BIGNUM - -enum { - MATH_OP_ABS, - MATH_OP_FLOOR, - MATH_OP_CEIL, - MATH_OP_ROUND, - MATH_OP_TRUNC, - MATH_OP_SQRT, - MATH_OP_FPROUND, - MATH_OP_ACOS, - MATH_OP_ASIN, - MATH_OP_ATAN, - MATH_OP_ATAN2, - MATH_OP_COS, - MATH_OP_EXP, - MATH_OP_LOG, - MATH_OP_POW, - MATH_OP_SIN, - MATH_OP_TAN, - MATH_OP_FMOD, - MATH_OP_REM, - MATH_OP_SIGN, - - MATH_OP_ADD, - MATH_OP_SUB, - MATH_OP_MUL, - MATH_OP_DIV, -}; - -static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, r_s, *r = &r_s; - JSFloatEnv *fe; - int rnd_mode; - JSValue op1; - - op1 = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigFloat(ctx, &a_s, op1); - fe = &ctx->fp_env; - if (argc > 1) { - fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); - if (!fe) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - } - - bf_init(ctx->bf_ctx, r); - switch (magic) { - case MATH_OP_ABS: - bf_set(r, a); - r->sign = 0; - break; - case MATH_OP_FLOOR: - rnd_mode = BF_RNDD; - goto rint; - case MATH_OP_CEIL: - rnd_mode = BF_RNDU; - goto rint; - case MATH_OP_ROUND: - rnd_mode = BF_RNDNU; - goto rint; - case MATH_OP_TRUNC: - rnd_mode = BF_RNDZ; - rint: - bf_set(r, a); - fe->status |= bf_rint(r, rnd_mode); - break; - case MATH_OP_SQRT: - fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); - break; - case MATH_OP_FPROUND: - bf_set(r, a); - fe->status |= bf_round(r, fe->prec, fe->flags); - break; - case MATH_OP_ACOS: - fe->status |= bf_acos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ASIN: - fe->status |= bf_asin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ATAN: - fe->status |= bf_atan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_COS: - fe->status |= bf_cos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_EXP: - fe->status |= bf_exp(r, a, fe->prec, fe->flags); - break; - case MATH_OP_LOG: - fe->status |= bf_log(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIN: - fe->status |= bf_sin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_TAN: - fe->status |= bf_tan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIGN: - if (bf_is_nan(a) || bf_is_zero(a)) { - bf_set(r, a); - } else { - bf_set_si(r, 1 - 2 * a->sign); - } - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return JS_NewBigFloat(ctx, r); -} - -static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; - JSFloatEnv *fe; - JSValue op1, op2; - - op1 = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - op2 = JS_ToNumber(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - a = JS_ToBigFloat(ctx, &a_s, op1); - b = JS_ToBigFloat(ctx, &b_s, op2); - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) { - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - } - - bf_init(ctx->bf_ctx, r); - switch (magic) { - case MATH_OP_ATAN2: - fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_POW: - fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUICKS); - break; - case MATH_OP_FMOD: - fe->status |= bf_fmod(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_REM: - fe->status |= bf_remainder(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_ADD: - fe->status |= bf_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_DIV: - fe->status |= bf_div(r, a, b, fe->prec, fe->flags); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_NewBigFloat(ctx, r); -} - -static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - BOOL is_max = magic; - JSValue val, ret; - int i; - uint32_t tag; - - if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); - } - - tag = JS_VALUE_GET_TAG(argv[0]); - if (tag == JS_TAG_INT) { - int a1, r1 = JS_VALUE_GET_INT(argv[0]); - for(i = 1; i < argc; i++) { - tag = JS_VALUE_GET_TAG(argv[i]); - if (tag != JS_TAG_INT) { - ret = JS_NewInt32(ctx, r1); - goto generic_case; - } - a1 = JS_VALUE_GET_INT(argv[i]); - if (is_max) - r1 = max_int(r1, a1); - else - r1 = min_int(r1, a1); - } - ret = JS_NewInt32(ctx, r1); - } else { - ret = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(ret)) - return ret; - i = 1; - generic_case: - for(; i < argc; i++) { - val = JS_ToNumber(ctx, argv[i]); - if (JS_IsException(val)) { - JS_FreeValue(ctx, ret); - return val; - } - if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(ret)) && - JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(val))) { - double r, a; - r = JS_VALUE_GET_FLOAT64(ret); - a = JS_VALUE_GET_FLOAT64(val); - if (!isnan(r)) { - if (isnan(a)) { - r = a; - } else { - if (is_max) - r = js_fmax(r, a); - else - r = js_fmin(r, a); - } - ret = __JS_NewFloat64(ctx, r); - } - } else { - bf_t a_s, *a, r_s, *r; - int res; - - r = JS_ToBigFloat(ctx, &r_s, ret); - if (!bf_is_nan(r)) { - a = JS_ToBigFloat(ctx, &a_s, val); - res = bf_cmp_full(a, r); - if (is_max) - res = -res; - if (bf_is_nan(a) || res < 0) { - JS_FreeValue(ctx, ret); - ret = JS_DupValue(ctx, val); - } - if (a == &a_s) - bf_delete(a); - } - if (r == &r_s) - bf_delete(r); - JS_FreeValue(ctx, val); - } - } - } - return ret; -} - -static JSValue js_math_abs(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - uint32_t tag; - - val = JS_ToNumeric(ctx, argv[0]); - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - if (JS_VALUE_GET_INT(val) < 0) - val = JS_NewInt64(ctx, -(int64_t)JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - val = __JS_NewFloat64(ctx, fabs(JS_VALUE_GET_FLOAT64(val))); - break; - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - r->sign = 0; - JS_FreeValue(ctx, val); - if (tag == JS_TAG_BIG_FLOAT) - val = JS_NewBigFloat(ctx, r); - else - val = JS_NewBigInt2(ctx, r, TRUE); - } - break; - default: - break; - } - return val; -} - -#if 0 -/* XXX: should give exact rounding */ -/* XXX: correct NaN/Infinity handling */ -static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - bf_t a_s, *a, r_s, *r = &r_s, r2_s, *r2 = &r2_s; - JSValue val; - int i; - BOOL is_float; - - bf_init(ctx->bf_ctx, r); - bf_set_si(r, 0); - for(i = 0; i < argc; i++) { - val = JS_ToNumber(ctx, argv[i]); - if (JS_IsException(val)) { - bf_delete(r); - return val; - } - a = JS_ToBigFloat(ctx, &is_float, &a_s, val); - bf_add(r, r, a, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - } - bf_init(ctx->bf_ctx, r2); - bf_sqrt(r2, r, ctx->fp_env.prec, ctx->fp_env.flags); - bf_delete(r); - return JS_NewBigFloat(ctx, r2); -} -#endif - -#else - static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -40133,8 +39867,6 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, } } -#endif /* !CONFIG_BIGNUM */ - static double js_math_sign(double a) { if (isnan(a) || a == 0.0) @@ -40267,11 +39999,7 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), -#ifdef CONFIG_BIGNUM - JS_CFUNC_DEF("abs", 1, js_math_abs ), -#else JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), -#endif JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), @@ -41959,46 +41687,12 @@ static JSValue json_parse_value(JSParseState *s) } is_neg = 1; number: -#ifdef CONFIG_BIGNUM - val = JS_DupValue(ctx, s->token.u.num.val); - if (is_neg) { - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p; - p = JS_VALUE_GET_PTR(val); - bf_neg(&p->num); - } - break; - case JS_TAG_FLOAT64: - { - double d; - d = JS_VALUE_GET_FLOAT64(val); - val = __JS_NewFloat64(ctx, -d); - } - break; - default: - case JS_TAG_INT: - { - int v; - v = JS_VALUE_GET_INT(val); - if (v == 0 && !is_bigint_mode(s->ctx)) - val = __JS_NewFloat64(ctx, -0.0); - else - val = JS_NewInt64(ctx, -(int64_t)v); - } - break; - } - } -#else val = s->token.u.num.val; if (is_neg) { double d; JS_ToFloat64(ctx, &d, val); /* no exception possible */ val = JS_NewFloat64(ctx, -d); } -#endif if (next_token(s)) goto fail; break; @@ -42168,10 +41862,6 @@ static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { return r; } -#ifdef CONFIG_BIGNUM -static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v); -#endif - static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, JSValueConst holder, JSValue val, JSValueConst key) { @@ -46764,7 +46454,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, double d; int p, i, c, sgn; JSString *sp; - + BOOL is_local; + rv = JS_NAN; s = JS_ToString(ctx, argv[0]); @@ -46780,11 +46471,15 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, if (string_get_signed_field(sp, &p, &fields[0])) goto done; + is_local = TRUE; for (i = 1; i < 6; i++) { if (string_get_field(sp, &p, &fields[i])) break; } - if (i == 6 && p < sp->len && string_get(sp, p) == '.') { + if (i <= 3) { + /* no time: UTC by default */ + is_local = FALSE; + } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') { /* parse milliseconds as a fractional part, round to nearest */ /* XXX: the spec does not indicate which rounding should be used */ int mul = 1000, ms = 0; @@ -46802,14 +46497,20 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, /* parse the time zone offset if present: [+-]HH:mm */ tz = 0; - if (p < sp->len && ((sgn = string_get(sp, p)) == '+' || sgn == '-')) { - if (string_get_field(sp, &p, &hh)) - goto done; - if (string_get_field(sp, &p, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; + if (p < sp->len) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + if (string_get_field(sp, &p, &hh)) + goto done; + if (string_get_field(sp, &p, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + is_local = FALSE; + } else if (sgn == 'Z') { + is_local = FALSE; + } } } else { /* toString or toUTCString format */ @@ -46844,6 +46545,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // XXX: parse optional milliseconds? /* parse the time zone offset if present: [+-]HHmm */ + is_local = FALSE; tz = 0; for (tz = 0; p < sp->len; p++) { sgn = string_get(sp, p); @@ -46860,7 +46562,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, } } } - d = set_date_fields(fields, 0) - tz * 60000; + d = set_date_fields(fields, is_local) - tz * 60000; rv = __JS_NewFloat64(ctx, d); done: @@ -47080,61 +46782,364 @@ void JS_AddIntrinsicEval(JSContext *ctx) #ifdef CONFIG_BIGNUM +/* Operators */ + +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i])); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->left.tab); + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->right.tab); + js_free_rt(rt, opset); + } +} + +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]), + mark_func); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + } +} + + +/* create an OperatorSet object */ +static JSValue js_operators_create_internal(JSContext *ctx, + int argc, JSValueConst *argv, + BOOL is_primitive) +{ + JSValue opset_obj, prop, obj; + JSOperatorSetData *opset, *opset1; + JSBinaryOperatorDef *def; + JSValueConst arg; + int i, j; + JSBinaryOperatorDefEntry *new_tab; + JSBinaryOperatorDefEntry *ent; + uint32_t op_count; + + if (ctx->rt->operator_count == UINT32_MAX) { + return JS_ThrowTypeError(ctx, "too many operators"); + } + opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET); + if (JS_IsException(opset_obj)) + goto fail; + opset = js_mallocz(ctx, sizeof(*opset)); + if (!opset) + goto fail; + JS_SetOpaque(opset_obj, opset); + if (argc >= 1) { + arg = argv[0]; + /* self operators */ + for(i = 0; i < JS_OVOP_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + opset->self_ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + /* left & right operators */ + for(j = 1; j < argc; j++) { + arg = argv[j]; + prop = JS_GetPropertyStr(ctx, arg, "left"); + if (JS_IsException(prop)) + goto fail; + def = &opset->right; + if (JS_IsUndefined(prop)) { + prop = JS_GetPropertyStr(ctx, arg, "right"); + if (JS_IsException(prop)) + goto fail; + if (JS_IsUndefined(prop)) { + JS_ThrowTypeError(ctx, "left or right property must be present"); + goto fail; + } + def = &opset->left; + } + /* get the operator set */ + obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype); + JS_FreeValue(ctx, prop); + if (JS_IsException(obj)) + goto fail; + prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + JS_FreeValue(ctx, obj); + if (JS_IsException(prop)) + goto fail; + opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET); + if (!opset1) { + JS_FreeValue(ctx, prop); + goto fail; + } + op_count = opset1->operator_counter; + JS_FreeValue(ctx, prop); + + /* we assume there are few entries */ + new_tab = js_realloc(ctx, def->tab, + (def->count + 1) * sizeof(def->tab[0])); + if (!new_tab) + goto fail; + def->tab = new_tab; + def->count++; + ent = def->tab + def->count - 1; + memset(ent, 0, sizeof(def->tab[0])); + ent->operator_index = op_count; + + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, + js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + ent->ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + opset->is_primitive = is_primitive; + opset->operator_counter = ctx->rt->operator_count++; + return opset_obj; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_operators_create_internal(ctx, argc, argv, FALSE); +} + +static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue opset_obj, prop; + JSOperatorSetData *opset; + const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; + JSOverloadableOperatorEnum op; + int i; + + opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset_obj)) + goto fail; + opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET); + if (!opset) + goto fail; + for(i = 0; i < countof(ops); i++) { + op = ops[i]; + prop = JS_GetPropertyStr(ctx, argv[0], + js_overloadable_operator_names[op]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (!JS_IsNull(prop) && check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + if (opset->self_ops[op]) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op])); + if (JS_IsNull(prop)) { + opset->self_ops[op] = NULL; + } else { + opset->self_ops[op] = JS_VALUE_GET_PTR(prop); + } + } + } + JS_FreeValue(ctx, opset_obj); + return JS_UNDEFINED; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static int js_operators_set_default(JSContext *ctx, JSValueConst obj) +{ + JSValue opset_obj; + + if (!JS_IsObject(obj)) /* in case the prototype is not defined */ + return 0; + opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE); + if (JS_IsException(opset_obj)) + return -1; + /* cannot be modified by the user */ + JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet, + opset_obj, 0); + return 0; +} + +static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); +} + +static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue func_obj, proto, opset_obj; + + func_obj = JS_UNDEFINED; + proto = JS_NewObject(ctx); + if (JS_IsException(proto)) + return JS_EXCEPTION; + opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE); + if (JS_IsException(opset_obj)) + goto fail; + JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet, + opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators", + 0, JS_CFUNC_constructor, 0); + if (JS_IsException(func_obj)) + goto fail; + JS_SetConstructor2(ctx, func_obj, proto, + 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, proto); + return func_obj; + fail: + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_operators_funcs[] = { + JS_CFUNC_DEF("create", 1, js_operators_create ), + JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ), +}; + +/* must be called after all overloadable base types are initialized */ +void JS_AddIntrinsicOperators(JSContext *ctx) +{ + JSValue obj; + + ctx->allow_operator_overloading = TRUE; + obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1); + JS_SetPropertyFunctionList(ctx, obj, + js_operators_funcs, + countof(js_operators_funcs)); + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators, + obj, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + /* add default operatorSets */ + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); +} + /* BigInt */ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) { uint32_t tag; - BOOL is_legacy; - int ret; - is_legacy = is_bigint_mode(ctx) ^ 1; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_INT: case JS_TAG_BOOL: - if (is_legacy) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigInt2(ctx, r, TRUE); - } else { - val = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - } + val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: case JS_TAG_BIG_FLOAT: { - bf_t *a, a_s, r_s, *r = &r_s; + bf_t *a, a_s; + a = JS_ToBigFloat(ctx, &a_s, val); - bf_init(ctx->bf_ctx, r); if (!bf_is_finite(a)) { JS_FreeValue(ctx, val); val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint"); } else { - bf_set(r, a); - ret = bf_rint(r, BF_RNDZ); + JSValue val1 = JS_NewBigInt(ctx); + bf_t *r; + int ret; + if (JS_IsException(val1)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + r = JS_GetBigInt(val1); + ret = bf_set(r, a); + ret |= bf_rint(r, BF_RNDZ); JS_FreeValue(ctx, val); - if (is_legacy && (ret & BF_ST_INEXACT)) { - bf_delete(r); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val1); + val = JS_ThrowOutOfMemory(ctx); + } else if (ret & BF_ST_INEXACT) { + JS_FreeValue(ctx, val1); val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); } else { - val = JS_NewBigInt2(ctx, r, is_legacy); + val = JS_CompactBigInt(ctx, val1); } } if (a == &a_s) bf_delete(a); } break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); break; case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, - is_legacy ? HINT_NUMBER : HINT_INTEGER); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) break; goto redo; @@ -47154,17 +47159,6 @@ static JSValue js_bigint_constructor(JSContext *ctx, return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); } -static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - if (tag == JS_TAG_BIG_INT) - return TRUE; - if (is_bigint_mode(ctx)) - return tag == JS_TAG_INT; - else - return FALSE; -} - static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) { if (JS_IsBigInt(ctx, this_val)) @@ -47173,8 +47167,6 @@ static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); if (p->class_id == JS_CLASS_BIG_INT) { - /* XXX: may relax the check in case the object comes from - bignum mode */ if (JS_IsBigInt(ctx, p->u.object_data)) return JS_DupValue(ctx, p->u.object_data); } @@ -47195,12 +47187,9 @@ static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } } ret = js_bigint_to_string1(ctx, val, base); JS_FreeValue(ctx, val); @@ -47220,72 +47209,96 @@ static JSValue js_bigint_div(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { - bf_t a_s, b_s, *a, *b, r_s, *r = &r_s, q_s, *q = &q_s; + bf_t a_s, b_s, *a, *b, *r, *q; int status; - + JSValue q_val, r_val; + + q_val = JS_NewBigInt(ctx); + if (JS_IsException(q_val)) + return JS_EXCEPTION; + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + goto fail; b = NULL; a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; + goto fail; b = JS_ToBigInt(ctx, &b_s, argv[1]); if (!b) { JS_FreeBigInt(ctx, a, &a_s); - return JS_EXCEPTION; + goto fail; } - bf_init(ctx->bf_ctx, q); - bf_init(ctx->bf_ctx, r); + q = JS_GetBigInt(q_val); + r = JS_GetBigInt(r_val); status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); JS_FreeBigInt(ctx, a, &a_s); JS_FreeBigInt(ctx, b, &b_s); if (unlikely(status)) { - bf_delete(q); - bf_delete(r); throw_bf_exception(ctx, status); - return JS_EXCEPTION; + goto fail; } + q_val = JS_CompactBigInt(ctx, q_val); if (magic & 0x10) { JSValue ret; - /* XXX: handle exceptions */ ret = JS_NewArray(ctx); - JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, q)); - JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, r)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, q_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); return ret; } else { - bf_delete(r); - return JS_NewBigInt(ctx, q); + JS_FreeValue(ctx, r_val); + return q_val; } + fail: + JS_FreeValue(ctx, q_val); + JS_FreeValue(ctx, r_val); + return JS_EXCEPTION; } static JSValue js_bigint_sqrt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { - bf_t a_s, *a, r_s, *r = &r_s, rem_s, *rem = &rem_s; + bf_t a_s, *a, *r, *rem; int status; + JSValue r_val, rem_val; + + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + return JS_EXCEPTION; + rem_val = JS_NewBigInt(ctx); + if (JS_IsException(rem_val)) + return JS_EXCEPTION; + r = JS_GetBigInt(r_val); + rem = JS_GetBigInt(rem_val); a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; - bf_init(ctx->bf_ctx, r); - bf_init(ctx->bf_ctx, rem); + goto fail; status = bf_sqrtrem(r, rem, a); JS_FreeBigInt(ctx, a, &a_s); if (unlikely(status & ~BF_ST_INEXACT)) { - bf_delete(r); - bf_delete(rem); - return throw_bf_exception(ctx, status); + throw_bf_exception(ctx, status); + goto fail; } + r_val = JS_CompactBigInt(ctx, r_val); if (magic) { JSValue ret; - /* XXX: handle exceptions */ ret = JS_NewArray(ctx); - JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, r)); - JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, rem)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, r_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); return ret; } else { - bf_delete(rem); - return JS_NewBigInt(ctx, r); + JS_FreeValue(ctx, rem_val); + return r_val; } + fail: + JS_FreeValue(ctx, r_val); + JS_FreeValue(ctx, rem_val); + return JS_EXCEPTION; } static JSValue js_bigint_op1(JSContext *ctx, @@ -47326,15 +47339,22 @@ static JSValue js_bigint_asUintN(JSContext *ctx, int argc, JSValueConst *argv, int asIntN) { uint64_t bits; - bf_t a_s, *a = &a_s, r_s, *r = &r_s, mask_s, *mask = &mask_s; - + bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; + JSValue res; + if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + r = JS_GetBigInt(res); a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) + if (!a) { + JS_FreeValue(ctx, res); return JS_EXCEPTION; + } /* XXX: optimize */ - bf_init(ctx->bf_ctx, r); + r = JS_GetBigInt(res); bf_init(ctx->bf_ctx, mask); bf_set_ui(mask, 1); bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); @@ -47351,7 +47371,7 @@ static JSValue js_bigint_asUintN(JSContext *ctx, } bf_delete(mask); JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt(ctx, r); + return JS_CompactBigInt(ctx, res); } static const JSCFunctionListEntry js_bigint_funcs[] = { @@ -47430,12 +47450,9 @@ static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } } ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); JS_FreeValue(ctx, val); @@ -47451,7 +47468,7 @@ static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigFloatValue(ctx, this_val); } -static int get_rnd_mode(JSContext *ctx, JSValueConst val) +static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) { int rnd_mode; if (JS_ToInt32Sat(ctx, &rnd_mode, val)) @@ -47468,8 +47485,7 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t f; - int res, rnd_mode; - bf_t a_s, *a, b; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47481,26 +47497,19 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; + /* XXX: swap parameter order for rounding mode and radix */ if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) - goto fail; - bf_init(ctx->bf_ctx, &b); - bf_set_float64(&b, 1e21); - res = bf_cmpu(a, &b); - bf_delete(&b); - if (a == &a_s) - bf_delete(a); - if (res >= 0) { - ret = JS_ToString(ctx, val); - } else { - ret = js_ftoa(ctx, val, 10, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; } + ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); JS_FreeValue(ctx, val); return ret; fail: @@ -47533,7 +47542,7 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t f; - int rnd_mode; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47551,12 +47560,18 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - ret = js_ftoa(ctx, val, 10, f + 1, + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, f + 1, rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); } JS_FreeValue(ctx, val); @@ -47571,7 +47586,7 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t p; - int rnd_mode; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47589,12 +47604,18 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - ret = js_ftoa(ctx, val, 10, p, rnd_mode | BF_FTOA_FORMAT_FIXED); + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); } JS_FreeValue(ctx, val); return ret; @@ -47617,10 +47638,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, { JSValue val; if (argc == 0) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); bf_set_zero(r, 0); - val = JS_NewBigFloat(ctx, r); } else { val = JS_DupValue(ctx, argv[0]); redo: @@ -47629,18 +47652,26 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, break; case JS_TAG_FLOAT64: { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_float64(r, JS_VALUE_GET_FLOAT64(val)); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + double d = JS_VALUE_GET_FLOAT64(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_float64(r, d)) + goto fail; } break; case JS_TAG_INT: { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + int32_t v = JS_VALUE_GET_INT(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_si(r, v)) + goto fail; } break; case JS_TAG_BIG_INT: @@ -47650,6 +47681,11 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); } break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; case JS_TAG_STRING: { const char *str, *p; @@ -47663,10 +47699,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, p = str; p += skip_spaces(p); if ((p - str) == len) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, 0); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); err = 0; } else { val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | @@ -47699,13 +47737,20 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, } } return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } static JSValue js_bigfloat_get_const(JSContext *ctx, - JSValueConst this_val, int magic) + JSValueConst this_val, int magic) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); + bf_t *r; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); switch(magic) { case 0: /* PI */ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); @@ -47741,13 +47786,13 @@ static JSValue js_bigfloat_get_const(JSContext *ctx, default: abort(); } - return JS_NewBigFloat(ctx, r); + return val; } static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - bf_t a_s, *a = &a_s; + bf_t *a; const char *str; JSValue ret; int radix; @@ -47771,10 +47816,13 @@ static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, if (!fe) goto fail; } - bf_init(ctx->bf_ctx, a); + ret = JS_NewBigFloat(ctx); + if (JS_IsException(ret)) + goto done; + a = JS_GetBigFloat(ret); /* XXX: use js_atof() */ bf_atof(a, str, NULL, radix, fe->prec, fe->flags); - ret = JS_NewBigFloat(ctx, a); + done: JS_FreeCString(ctx, str); return ret; } @@ -47800,7 +47848,201 @@ static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) return JS_FALSE; p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_finite(&p->num)); + return JS_NewBool(ctx, bf_is_nan(&p->num)); +} + +enum { + MATH_OP_ABS, + MATH_OP_FLOOR, + MATH_OP_CEIL, + MATH_OP_ROUND, + MATH_OP_TRUNC, + MATH_OP_SQRT, + MATH_OP_FPROUND, + MATH_OP_ACOS, + MATH_OP_ASIN, + MATH_OP_ATAN, + MATH_OP_ATAN2, + MATH_OP_COS, + MATH_OP_EXP, + MATH_OP_LOG, + MATH_OP_POW, + MATH_OP_SIN, + MATH_OP_TAN, + MATH_OP_FMOD, + MATH_OP_REM, + MATH_OP_SIGN, + + MATH_OP_ADD, + MATH_OP_SUB, + MATH_OP_MUL, + MATH_OP_DIV, +}; + +static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, *r; + JSFloatEnv *fe; + int rnd_mode; + JSValue op1, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigFloat(ctx, &a_s, op1); + fe = &ctx->fp_env; + if (argc > 1) { + fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ABS: + bf_set(r, a); + r->sign = 0; + break; + case MATH_OP_FLOOR: + rnd_mode = BF_RNDD; + goto rint; + case MATH_OP_CEIL: + rnd_mode = BF_RNDU; + goto rint; + case MATH_OP_ROUND: + rnd_mode = BF_RNDNA; + goto rint; + case MATH_OP_TRUNC: + rnd_mode = BF_RNDZ; + rint: + bf_set(r, a); + fe->status |= bf_rint(r, rnd_mode); + break; + case MATH_OP_SQRT: + fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); + break; + case MATH_OP_FPROUND: + bf_set(r, a); + fe->status |= bf_round(r, fe->prec, fe->flags); + break; + case MATH_OP_ACOS: + fe->status |= bf_acos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ASIN: + fe->status |= bf_asin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ATAN: + fe->status |= bf_atan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_COS: + fe->status |= bf_cos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_EXP: + fe->status |= bf_exp(r, a, fe->prec, fe->flags); + break; + case MATH_OP_LOG: + fe->status |= bf_log(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIN: + fe->status |= bf_sin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_TAN: + fe->status |= bf_tan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIGN: + if (bf_is_nan(a) || bf_is_zero(a)) { + bf_set(r, a); + } else { + bf_set_si(r, 1 - 2 * a->sign); + } + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return res; +} + +static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; + JSFloatEnv *fe; + JSValue op1, op2, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + op2 = JS_ToNumeric(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ATAN2: + fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_POW: + fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); + break; + case MATH_OP_FMOD: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; + case MATH_OP_REM: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); + break; + case MATH_OP_ADD: + fe->status |= bf_add(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_SUB: + fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_MUL: + fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_DIV: + fe->status |= bf_div(r, a, b, fe->prec, fe->flags); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; } static const JSCFunctionListEntry js_bigfloat_funcs[] = { @@ -47895,9 +48137,6 @@ static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); } -/* temporary fix for string conversion overflows */ -#define BF_EXP_BITS_MAX1 (BF_EXP_BITS_MAX - 1) - static JSValue js_float_env_setPrec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -47913,18 +48152,16 @@ static JSValue js_float_env_setPrec(JSContext *ctx, return JS_EXCEPTION; if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX1; + exp_bits = BF_EXP_BITS_MAX; if (argc > 2 && !JS_IsUndefined(argv[2])) { if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX1) + if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); } - flags = BF_RNDN | bf_set_exp_bits(exp_bits); - if (exp_bits != BF_EXP_BITS_MAX1) - flags |= BF_FLAG_SUBNORMAL; + flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); saved_prec = ctx->fp_env.prec; saved_flags = ctx->fp_env.flags; @@ -47984,24 +48221,20 @@ static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_v case FE_EXP: if (JS_ToInt32Sat(ctx, &b, val)) return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX1) + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - if (b == BF_EXP_BITS_MAX1) - fe->flags &= ~BF_FLAG_SUBNORMAL; fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | bf_set_exp_bits(b); break; case FE_RNDMODE: - b = get_rnd_mode(ctx, val); + b = bigfloat_get_rnd_mode(ctx, val); if (b < 0) return JS_EXCEPTION; fe->flags = (fe->flags & ~BF_RND_MASK) | b; break; case FE_SUBNORMAL: b = JS_ToBool(ctx, val); - if (bf_get_exp_bits(fe->flags) != BF_EXP_BITS_MAX1) { - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - } + fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); break; default: b = JS_ToBool(ctx, val); @@ -48031,12 +48264,12 @@ static const JSCFunctionListEntry js_float_env_funcs[] = { JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), - JS_PROP_INT32_DEF("RNDNU", BF_RNDNU, 0 ), + JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX1, 0 ), + JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), }; static const JSCFunctionListEntry js_float_env_proto_funcs[] = { @@ -48112,10 +48345,18 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, case JS_TAG_BOOL: case JS_TAG_INT: { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); - bfdec_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigDecimal(ctx, r); + bfdec_t *r; + int32_t v = JS_VALUE_GET_INT(val); + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); + if (bfdec_set_si(r, v)) { + JS_FreeValue(ctx, val); + val = JS_EXCEPTION; + break; + } } break; case JS_TAG_FLOAT64: @@ -48138,10 +48379,12 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, p = str; p += skip_spaces(p); if ((p - str) == len) { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); bfdec_set_zero(r, 0); - val = JS_NewBigDecimal(ctx, r); err = 0; } else { val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); @@ -48166,12 +48409,14 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, goto redo; case JS_TAG_UNDEFINED: { - bfdec_t r_s, *r = &r_s; + bfdec_t *r; if (!allow_null_or_undefined) goto fail; - bfdec_init(ctx->bf_ctx, r); + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); bfdec_set_nan(r); - val = JS_NewBigDecimal(ctx, r); } break; default: @@ -48188,10 +48433,12 @@ static JSValue js_bigdecimal_constructor(JSContext *ctx, { JSValue val; if (argc == 0) { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigDecimal(val); bfdec_set_zero(r, 0); - val = JS_NewBigDecimal(ctx, r); } else { val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE); } @@ -48230,6 +48477,39 @@ static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigDecimalValue(ctx, this_val); } +static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) +{ + const char *str; + size_t size; + int rnd_mode; + + str = JS_ToCStringLen(ctx, &size, obj); + if (!str) + return -1; + if (strlen(str) != size) + goto invalid_rounding_mode; + if (!strcmp(str, "floor")) { + rnd_mode = BF_RNDD; + } else if (!strcmp(str, "ceiling")) { + rnd_mode = BF_RNDU; + } else if (!strcmp(str, "down")) { + rnd_mode = BF_RNDZ; + } else if (!strcmp(str, "up")) { + rnd_mode = BF_RNDA; + } else if (!strcmp(str, "half-even")) { + rnd_mode = BF_RNDN; + } else if (!strcmp(str, "half-up")) { + rnd_mode = BF_RNDNA; + } else { + invalid_rounding_mode: + JS_FreeCString(ctx, str); + JS_ThrowTypeError(ctx, "invalid rounding mode"); + return -1; + } + JS_FreeCString(ctx, str); + return rnd_mode; +} + typedef struct { int64_t prec; bf_flags_t flags; @@ -48239,10 +48519,9 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, JSValueConst obj) { JSValue prop; - const char *str; - size_t size; int64_t val; BOOL has_prec; + int rnd_mode; if (!JS_IsObject(obj)) { JS_ThrowTypeErrorNotAnObject(ctx); @@ -48251,29 +48530,11 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); if (JS_IsException(prop)) return -1; - str = JS_ToCStringLen(ctx, &size, prop); + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); JS_FreeValue(ctx, prop); - if (!str) + if (rnd_mode < 0) return -1; - if (strlen(str) != size) - goto invalid_rounding_mode; - if (!strcmp(str, "floor")) { - fe->flags = BF_RNDD; - } else if (!strcmp(str, "ceiling")) { - fe->flags = BF_RNDU; - } else if (!strcmp(str, "down")) { - fe->flags = BF_RNDZ; - } else if (!strcmp(str, "half-even")) { - fe->flags = BF_RNDN; - } else if (!strcmp(str, "half-up")) { - fe->flags = BF_RNDNA; - } else { - invalid_rounding_mode: - JS_FreeCString(ctx, str); - JS_ThrowTypeError(ctx, "invalid rounding mode"); - return -1; - } - JS_FreeCString(ctx, str); + fe->flags = rnd_mode; prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); if (JS_IsException(prop)) @@ -48282,7 +48543,7 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, if (!JS_IsUndefined(prop)) { if (JS_ToInt64SatFree(ctx, &val, prop)) return -1; - if (val < 0 || val > BF_PREC_MAX) + if (val < 1 || val > BF_PREC_MAX) goto invalid_precision; fe->prec = val; has_prec = TRUE; @@ -48320,7 +48581,7 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { bfdec_t *a, *b, r_s, *r = &r_s; - JSValue op1, op2; + JSValue op1, op2, res; BigDecimalEnv fe_s, *fe = &fe_s; int op_count, ret; @@ -48354,21 +48615,18 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, fe->flags = BF_RNDZ; fe->prec = BF_PREC_INF; if (op_count < argc) { - if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) { - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - if ((fe->flags & BF_FLAG_RADPNT_PREC) && - magic != MATH_OP_DIV && - magic != MATH_OP_ROUND) { - JS_ThrowTypeError(ctx, "maximumFractionDigits is not supported for this operation"); + if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) goto fail; - } } - bfdec_init(ctx->bf_ctx, r); + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigDecimal(res); switch (magic) { case MATH_OP_ADD: ret = bfdec_add(r, a, b, fe->prec, fe->flags); @@ -48382,6 +48640,9 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, case MATH_OP_DIV: ret = bfdec_div(r, a, b, fe->prec, fe->flags); break; + case MATH_OP_FMOD: + ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; case MATH_OP_SQRT: ret = bfdec_sqrt(r, a, fe->prec, fe->flags); break; @@ -48398,16 +48659,119 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | BF_ST_OVERFLOW; if (ret != 0) { - bfdec_delete(r); + JS_FreeValue(ctx, res); return throw_bf_exception(ctx, ret); } else { - return JS_NewBigDecimal(ctx, r); + return res; } } +static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (JS_IsUndefined(argv[0])) { + ret = js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); + } else { + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f + 1, + rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t p; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_IsUndefined(argv[0])) { + return JS_ToStringFree(ctx, val); + } + if (JS_ToInt64Sat(ctx, &p, argv[0])) + goto fail; + if (p < 1 || p > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, p, + rnd_mode | BF_FTOA_FORMAT_FIXED); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), }; static const JSCFunctionListEntry js_bigdecimal_funcs[] = { @@ -48415,6 +48779,7 @@ static const JSCFunctionListEntry js_bigdecimal_funcs[] = { JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), + JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), }; @@ -49512,6 +49877,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, count = min_int(final - from, len - to); if (count > 0) { p = JS_VALUE_GET_OBJ(this_val); + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); shift = typed_array_size_log2(p->class_id); memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), @@ -49579,6 +49946,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + shift = typed_array_size_log2(p->class_id); switch(shift) { case 0: @@ -53,7 +53,7 @@ typedef struct JSClass JSClass; typedef uint32_t JSClassID; typedef uint32_t JSAtom; -#if defined(__x86_64__) || defined(__aarch64__) +#if INTPTR_MAX >= INT64_MAX #define JS_PTR64 #define JS_PTR64_DEF(a) a #else @@ -366,7 +366,9 @@ void JS_AddIntrinsicPromise(JSContext *ctx); void JS_AddIntrinsicBigInt(JSContext *ctx); void JS_AddIntrinsicBigFloat(JSContext *ctx); void JS_AddIntrinsicBigDecimal(JSContext *ctx); -/* enable "use bigint", "use math" and operator overloading */ +/* enable operator overloading */ +void JS_AddIntrinsicOperators(JSContext *ctx); +/* enable "use math" */ void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, @@ -531,12 +533,16 @@ static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) return v; } -JS_BOOL JS_IsNumber(JSValueConst v); +static inline JS_BOOL JS_IsNumber(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +} -static inline JS_BOOL JS_IsInteger(JSValueConst v) +static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) { int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT; + return tag == JS_TAG_BIG_INT; } static inline JS_BOOL JS_IsBigFloat(JSValueConst v) @@ -1,8 +1,8 @@ /* * QuickJS Read Eval Print Loop * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 Charlie Gordon + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -860,18 +860,6 @@ import * as os from "os"; var hex_mode = false; var eval_mode = "std"; - function bignum_typeof(a) { - "use bigint"; - return typeof a; - } - - function eval_mode_typeof(a) { - if (eval_mode === "std") - return typeof a; - else - return bignum_typeof(a); - } - function number_to_string(a, radix) { var s; if (!isFinite(a)) { @@ -897,13 +885,6 @@ import * as os from "os"; s = a.toString(); } } - if (eval_mode !== "std" && s.indexOf(".") < 0 && - ((radix == 16 && s.indexOf("p") < 0) || - (radix == 10 && s.indexOf("e") < 0))) { - /* add a decimal point so that the floating point type - is visible */ - s += ".0"; - } return s; } } @@ -975,7 +956,7 @@ import * as os from "os"; function print_rec(a) { var n, i, keys, key, type, s; - type = eval_mode_typeof(a); + type = typeof(a); if (type === "object") { if (a === null) { std.puts(a); @@ -1037,7 +1018,7 @@ import * as os from "os"; } else if (type === "bigfloat") { std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10)); } else if (type === "bigdecimal") { - std.puts(a.toString() + "d"); + std.puts(a.toString() + "m"); } else if (type === "symbol") { std.puts(String(a)); } else if (type === "function") { @@ -1133,8 +1114,7 @@ import * as os from "os"; param = expr.substring(cmd.length + 1).trim(); if (param === "") { std.puts("Running mode=" + eval_mode + "\n"); - } else if (param === "std" || param === "math" || - param === "bigint") { + } else if (param === "std" || param === "math") { eval_mode = param; } else { std.puts("Invalid mode\n"); @@ -1192,7 +1172,7 @@ import * as os from "os"; std.puts("\\p [m [e]] set the BigFloat precision to 'm' bits\n" + "\\digits n set the BigFloat precision to 'ceil(n*log2(10))' bits\n"); if (!has_jscalc) { - std.puts("\\mode [std|bigint|math] change the running mode (current = " + eval_mode + ")\n"); + std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n"); } } if (!config_numcalc) { @@ -1206,8 +1186,6 @@ import * as os from "os"; try { if (eval_mode === "math") expr = '"use math"; void 0;' + expr; - else if (eval_mode === "bigint") - expr = '"use bigint"; void 0;' + expr; var now = (new Date).getTime(); /* eval as a script */ result = std.evalScript(expr, { backtrace_barrier: true }); diff --git a/test262_errors.txt b/test262_errors.txt index 90adb53..4b20792 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -1,4 +1,17 @@ +test262/test/language/expressions/arrow-function/eval-var-scope-syntax-err.js:47: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/async-generator/eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/async-generator/named-eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/class/super-evaluation-order.js:26: Test262Error: via ArgumentListEvaluation Expected SameValue(«0», «123») to be true +test262/test/language/expressions/class/super-evaluation-order.js:26: strict mode: Test262Error: via ArgumentListEvaluation Expected SameValue(«0», «123») to be true test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called +test262/test/language/expressions/function/eval-var-scope-syntax-err.js:48: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/object/method-definition/async-gen-meth-eval-var-scope-syntax-err.js:32: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/object/method-definition/gen-meth-eval-var-scope-syntax-err.js:54: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/expressions/object/method-definition/meth-eval-var-scope-syntax-err.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: value has no property test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: value has no property +test262/test/language/statements/async-generator/eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/statements/function/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all +test262/test/language/statements/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all diff --git a/tests/test_bignum.js b/tests/test_bignum.js index 0745929..f4f72a0 100644 --- a/tests/test_bignum.js +++ b/tests/test_bignum.js @@ -171,67 +171,152 @@ function test_bigfloat() e = new BigFloatEnv(128); assert(e.prec == 128); a = BigFloat.sqrt(2l, e); - assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e)); + assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e)); assert(e.inexact === true); assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l); b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128); - assert(a == b); + assert(a === b); + + assert(BigFloat.isNaN(BigFloat(NaN))); + assert(BigFloat.isFinite(1l)); + assert(!BigFloat.isFinite(1l/0l)); + + assert(BigFloat.abs(-3l) === 3l); + assert(BigFloat.sign(-3l) === -1l); + + assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l); + assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l); + assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l); + + assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l); + assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l); + assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l); + + assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l); + assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l); + assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l); + assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l); + + assert(BigFloat.floor(2.5l) === 2l); + assert(BigFloat.ceil(2.5l) === 3l); + assert(BigFloat.trunc(-2.5l) === -2l); + assert(BigFloat.round(2.5l) === 3l); + + assert(BigFloat.fmod(3l,2l) === 1l); + assert(BigFloat.remainder(3l,2l) === -1l); + + /* string conversion */ + assert((1234.125l).toString(), "1234.125"); + assert((1234.125l).toFixed(2), "1234.13"); + assert((1234.125l).toFixed(2, "down"), "1234.12"); + assert((1234.125l).toExponential(), "1.234125e+3"); + assert((1234.125l).toExponential(5), "1.23413e+3"); + assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3"); + assert((1234.125l).toPrecision(6), "1234.13"); + assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12"); + + /* string conversion with binary base */ + assert((0x123.438l).toString(16), "123.438"); + assert((0x323.438l).toString(16), "323.438"); + assert((0x723.438l).toString(16), "723.438"); + assert((0xf23.438l).toString(16), "f23.438"); + assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44"); + assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44"); + assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44"); + assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44"); + assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044"); + assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0"); + assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44"); + assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43"); + assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44"); + assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44"); + assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44"); + assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8"); } function test_bigdecimal() { - assert(1d === 1d); - assert(1d !== 2d); - test_less(1d, 2d); - test_eq(2d, 2d); + assert(1m === 1m); + assert(1m !== 2m); + test_less(1m, 2m); + test_eq(2m, 2m); - test_less(1, 2d); - test_eq(2, 2d); + test_less(1, 2m); + test_eq(2, 2m); - test_less(1.1, 2d); - test_eq(Math.sqrt(4), 2d); + test_less(1.1, 2m); + test_eq(Math.sqrt(4), 2m); - test_less(2n, 3d); - test_eq(3n, 3d); + test_less(2n, 3m); + test_eq(3n, 3m); - assert(BigDecimal("1234.1") === 1234.1d); - assert(BigDecimal(" 1234.1") === 1234.1d); - assert(BigDecimal(" 1234.1 ") === 1234.1d); - - assert(BigDecimal(0.1) === 0.1d); - assert(BigDecimal(123) === 123d); - assert(BigDecimal(true) === 1d); + assert(BigDecimal("1234.1") === 1234.1m); + assert(BigDecimal(" 1234.1") === 1234.1m); + assert(BigDecimal(" 1234.1 ") === 1234.1m); - assert(123d + 1d === 124d); - assert(123d - 1d === 122d); + assert(BigDecimal(0.1) === 0.1m); + assert(BigDecimal(123) === 123m); + assert(BigDecimal(true) === 1m); - assert(3.2d * 3d === 9.6d); - assert(10d / 2d === 5d); - assertThrows(RangeError, () => { 10d / 3d } ); - assert(BigDecimal.div(20d, 3d, - { roundingMode: "half-even", - maximumSignificantDigits: 3 }) === 6.67d); - assert(BigDecimal.div(20d, 3d, - { roundingMode: "half-even", - maximumFractionDigits: 3 }) === 6.667d); + assert(123m + 1m === 124m); + assert(123m - 1m === 122m); - assert(10d % 3d === 1d); - assert(-10d % 3d === -1d); + assert(3.2m * 3m === 9.6m); + assert(10m / 2m === 5m); + assertThrows(RangeError, () => { 10m / 3m } ); - assert(-10d % 3d === -1d); + assert(10m % 3m === 1m); + assert(-10m % 3m === -1m); - assert(1234.5d ** 3d === 1881365963.625d); - assertThrows(RangeError, () => { 2d ** 3.1d } ); - assertThrows(RangeError, () => { 2d ** -3d } ); + assert(1234.5m ** 3m === 1881365963.625m); + assertThrows(RangeError, () => { 2m ** 3.1m } ); + assertThrows(RangeError, () => { 2m ** -3m } ); - assert(BigDecimal.sqrt(2d, + assert(BigDecimal.sqrt(2m, + { roundingMode: "half-even", + maximumSignificantDigits: 4 }) === 1.414m); + assert(BigDecimal.sqrt(101m, { roundingMode: "half-even", - maximumSignificantDigits: 4 }) === 1.414d); + maximumFractionDigits: 3 }) === 10.050m); + assert(BigDecimal.sqrt(0.002m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 0.045m); - assert(BigDecimal.round(3.14159d, + assert(BigDecimal.round(3.14159m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 3.142m); + + assert(BigDecimal.add(3.14159m, 0.31212m, + { roundingMode: "half-even", + maximumFractionDigits: 2 }) === 3.45m); + assert(BigDecimal.sub(3.14159m, 0.31212m, + { roundingMode: "down", + maximumFractionDigits: 2 }) === 2.82m); + assert(BigDecimal.mul(3.14159m, 0.31212m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 0.981m); + assert(BigDecimal.mod(3.14159m, 0.31211m, + { roundingMode: "half-even", + maximumFractionDigits: 4 }) === 0.0205m); + assert(BigDecimal.div(20m, 3m, + { roundingMode: "half-even", + maximumSignificantDigits: 3 }) === 6.67m); + assert(BigDecimal.div(20m, 3m, { roundingMode: "half-even", - maximumFractionDigits: 3 }) === 3.142d); + maximumFractionDigits: 50 }) === + 6.66666666666666666666666666666666666666666666666667m); + + /* string conversion */ + assert((1234.125m).toString(), "1234.125"); + assert((1234.125m).toFixed(2), "1234.13"); + assert((1234.125m).toFixed(2, "down"), "1234.12"); + assert((1234.125m).toExponential(), "1.234125e+3"); + assert((1234.125m).toExponential(5), "1.23413e+3"); + assert((1234.125m).toExponential(5, "down"), "1.23412e+3"); + assert((1234.125m).toPrecision(6), "1234.13"); + assert((1234.125m).toPrecision(6, "down"), "1234.12"); + assert((-1234.125m).toPrecision(6, "floor"), "-1234.13"); } test_bigint1(); diff --git a/tests/test_op_overloading.js b/tests/test_op_overloading.js new file mode 100644 index 0000000..d08a85e --- /dev/null +++ b/tests/test_op_overloading.js @@ -0,0 +1,207 @@ +"use strict"; + +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +/* operators overloading with Operators.create() */ +function test_operators_create() { + class Vec2 + { + constructor(x, y) { + this.x = x; + this.y = y; + } + static mul_scalar(p1, a) { + var r = new Vec2(); + r.x = p1.x * a; + r.y = p1.y * a; + return r; + } + toString() { + return "Vec2(" + this.x + "," + this.y + ")"; + } + } + + Vec2.prototype[Symbol.operatorSet] = Operators.create( + { + "+"(p1, p2) { + var r = new Vec2(); + r.x = p1.x + p2.x; + r.y = p1.y + p2.y; + return r; + }, + "-"(p1, p2) { + var r = new Vec2(); + r.x = p1.x - p2.x; + r.y = p1.y - p2.y; + return r; + }, + "=="(a, b) { + return a.x == b.x && a.y == b.y; + }, + "<"(a, b) { + var r; + /* lexicographic order */ + if (a.x == b.x) + r = (a.y < b.y); + else + r = (a.x < b.x); + return r; + }, + "++"(a) { + var r = new Vec2(); + r.x = a.x + 1; + r.y = a.y + 1; + return r; + } + }, + { + left: Number, + "*"(a, b) { + return Vec2.mul_scalar(b, a); + } + }, + { + right: Number, + "*"(a, b) { + return Vec2.mul_scalar(a, b); + } + }); + + var a = new Vec2(1, 2); + var b = new Vec2(3, 4); + var r; + + r = a * 2 + 3 * b; + assert(r.x === 11 && r.y === 16); + assert(a == a, true); + assert(a == b, false); + assert(a != a, false); + assert(a < b, true); + assert(a <= b, true); + assert(b < a, false); + assert(b <= a, false); + assert(a <= a, true); + assert(a >= a, true); + a++; + assert(a.x === 2 && a.y === 3); + r = ++a; + assert(a.x === 3 && a.y === 4); + assert(r === a); +} + +/* operators overloading thru inheritance */ +function test_operators() +{ + var Vec2; + + function mul_scalar(p1, a) { + var r = new Vec2(); + r.x = p1.x * a; + r.y = p1.y * a; + return r; + } + + var vec2_ops = Operators({ + "+"(p1, p2) { + var r = new Vec2(); + r.x = p1.x + p2.x; + r.y = p1.y + p2.y; + return r; + }, + "-"(p1, p2) { + var r = new Vec2(); + r.x = p1.x - p2.x; + r.y = p1.y - p2.y; + return r; + }, + "=="(a, b) { + return a.x == b.x && a.y == b.y; + }, + "<"(a, b) { + var r; + /* lexicographic order */ + if (a.x == b.x) + r = (a.y < b.y); + else + r = (a.x < b.x); + return r; + }, + "++"(a) { + var r = new Vec2(); + r.x = a.x + 1; + r.y = a.y + 1; + return r; + } + }, + { + left: Number, + "*"(a, b) { + return mul_scalar(b, a); + } + }, + { + right: Number, + "*"(a, b) { + return mul_scalar(a, b); + } + }); + + Vec2 = class Vec2 extends vec2_ops + { + constructor(x, y) { + super(); + this.x = x; + this.y = y; + } + toString() { + return "Vec2(" + this.x + "," + this.y + ")"; + } + } + + var a = new Vec2(1, 2); + var b = new Vec2(3, 4); + var r; + + r = a * 2 + 3 * b; + assert(r.x === 11 && r.y === 16); + assert(a == a, true); + assert(a == b, false); + assert(a != a, false); + assert(a < b, true); + assert(a <= b, true); + assert(b < a, false); + assert(b <= a, false); + assert(a <= a, true); + assert(a >= a, true); + a++; + assert(a.x === 2 && a.y === 3); + r = ++a; + assert(a.x === 3 && a.y === 4); + assert(r === a); +} + +function test_default_op() +{ + assert(Object(1) + 2, 3); + assert(Object(1) + true, 2); + assert(-Object(1), -1); +} + +test_operators_create(); +test_operators(); +test_default_op(); diff --git a/tests/test_qjscalc.js b/tests/test_qjscalc.js index f7bf804..1483466 100644 --- a/tests/test_qjscalc.js +++ b/tests/test_qjscalc.js @@ -66,13 +66,14 @@ function test_integer() r = (1 << 31) < 0; assert(r, false, "(1 << 31) < 0 === false"); + + assert(typeof 1 === "number"); + assert(typeof 9007199254740991 === "number"); + assert(typeof 9007199254740992 === "bigint"); } function test_float() { - var e, a, b, sqrt2; - - assert(typeof 1 === "bigint"); assert(typeof 1.0 === "bigfloat"); assert(1 == 1.0); assert(1 !== 1.0); @@ -101,6 +102,16 @@ function test_modulo() assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]); } +function test_fraction() +{ + assert((1/3 + 1).toString(), "4/3") + assert((2/3)^30, 1073741824/205891132094649); + assert(1/3 < 2/3); + assert(1/3 < 1); + assert(1/3 == 1.0/3); + assert(1.0/3 < 2/3); +} + function test_mod() { var a, b, p; @@ -235,6 +246,7 @@ test_integer(); test_float(); test_modulo(); +test_fraction(); test_mod(); test_polynomial(); test_poly_mod(); |