├── LICENSE ├── README.md ├── __tests__ ├── __snapshots__ │ └── index-test.js.snap └── index-test.js ├── index.js ├── package-lock.json └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transform-bigint 2 | 3 | *Update:* Now it can convert a code using BigInt into a code using JSBI (https://github.com/GoogleChromeLabs/jsbi). 4 | It will try to detect when an operator is used for bigints, not numbers. This will not work in many cases, so please use JSBI directly only 5 | if you know, that the code works only with bigints. 6 | 7 | An example from https://github.com/GoogleChromeLabs/babel-plugin-transform-jsbi-to-bigint: 8 | ========================================================================================== 9 | 10 | Input using native `BigInt`s: 11 | 12 | ```javascript 13 | const a = BigInt(Number.MAX_SAFE_INTEGER); 14 | const b = 2n; 15 | 16 | a + b; 17 | a - b; 18 | a * b; 19 | a / b; 20 | a % b; 21 | a ** b; 22 | a << b; 23 | a >> b; 24 | a & b; 25 | a | b; 26 | a ^ b; 27 | 28 | -a; 29 | ~a; 30 | 31 | a === b; 32 | a < b; 33 | a <= b; 34 | a > b; 35 | a >= b; 36 | 37 | a.toString(); 38 | Number(a); 39 | ``` 40 | 41 | Compiled output using `JSBI`: 42 | 43 | ``` 44 | const a = JSBI.BigInt(Number.MAX_SAFE_INTEGER); 45 | const b = JSBI.BigInt(2); 46 | JSBI.add(a, b); 47 | JSBI.subtract(a, b); 48 | JSBI.multiply(a, b); 49 | JSBI.divide(a, b); 50 | JSBI.remainder(a, b); 51 | JSBI.exponentiate(a, b); 52 | JSBI.leftShift(a, b); 53 | JSBI.signedRightShift(a, b); 54 | JSBI.bitwiseAnd(a, b); 55 | JSBI.bitwiseOr(a, b); 56 | JSBI.bitwiseXor(a, b); 57 | JSBI.unaryMinus(a); 58 | JSBI.bitwiseNot(a); 59 | JSBI.equal(a, b); 60 | JSBI.lessThan(a, b); 61 | JSBI.lessThanOrEqual(a, b); 62 | JSBI.greaterThan(a, b); 63 | JSBI.greaterThanOrEqual(a, b); 64 | a.toString(); 65 | JSBI.toNumber(a); 66 | ``` 67 | 68 | Playground: 69 | =========== 70 | 1. Open https://babeljs.io/en/repl 71 | 2. Turn off all presets. 72 | 3. "Add plugin" @babel/babel-plugin-syntax-bigint 73 | 4. "Add plugin" babel-plugin-transform-bigint 74 | 75 | 76 | ¡ It is buggy ! 77 | 78 | Example: 79 | ======== 80 | 81 | 1. Create a folder "example". 82 | 2. Create a file test.js: 83 | ```javascript 84 | // floor(log2(n)), n >= 1 85 | function ilog2(n) { 86 | let i = 0n; 87 | while (n >= 2n**(2n**i)) { 88 | i += 1n; 89 | } 90 | let e = 0n; 91 | let t = 1n; 92 | while (i >= 0n) { 93 | let b = 2n**i; 94 | if (n >= t * 2n**b) { 95 | t *= 2n**b; 96 | e += b; 97 | } 98 | i -= 1n; 99 | } 100 | return e; 101 | } 102 | 103 | // floor(sqrt(S)), S >= 0, https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method 104 | function sqrt(S) { 105 | let e = ilog2(S); 106 | if (e < 2n) { 107 | return 1n; 108 | } 109 | let f = e / 4n + 1n; 110 | let x = (sqrt(S / 2n**(f * 2n)) + 1n) * 2n**f; 111 | let xprev = x + 1n; 112 | while (xprev > x) { 113 | xprev = x; 114 | x = (x + S / x) / 2n; 115 | } 116 | return xprev; 117 | } 118 | 119 | function squareRoot(value, decimalDigits) { 120 | return (sqrt(BigInt(value) * 10n**(BigInt(decimalDigits) * 2n + 2n)) + 5n).toString(); 121 | } 122 | 123 | 124 | ``` 125 | 126 | 3. Use babel: 127 | ```sh 128 | npm init 129 | npm install --save https://github.com/Yaffle/babel-plugin-transform-bigint 130 | npm install --save-dev @babel/core @babel/cli 131 | npm install jsbi --save 132 | npm install --global http-server 133 | npx babel --plugins=babel-plugin-transform-bigint test.js > test-transformed.js 134 | http-server -p 8081 135 | ``` 136 | 137 | 4. Comment out the next line in test-transformed.js: 138 | ```javascript 139 | import JSBI from "./jsbi.mjs"; 140 | ``` 141 | 142 | 5. Create a file `test.html`. 143 | ```html 144 | 145 | 146 | 147 | 148 | 149 | 150 | 163 | 168 | 169 | 170 |
171 |
172 | 173 | 174 |
175 |
176 | 177 | 178 |
179 |
180 | 181 |
182 |
183 | 184 | 185 | ``` 186 | 187 | 6. Open `http://127.0.0.1:8081/test.html` in a web browser. 188 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/index-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`BigInt.asUintN(64, a), BigInt.asIntN(64, a) 1`] = ` 4 | "import JSBI from \\"./jsbi.mjs\\"; 5 | const a = JSBI.BigInt(1); 6 | console.log(JSBI.asUintN(64, a)); 7 | console.log(JSBI.asIntN(64, a));" 8 | `; 9 | 10 | exports[`BigInt.asUintN(n, a) is replaced 1`] = ` 11 | "import JSBI from \\"./jsbi.mjs\\"; 12 | const g = JSBI.BigInt(1); 13 | JSBI.asUintN(10, g);" 14 | `; 15 | 16 | exports[`CallExpression's type 1`] = ` 17 | "var maybeJSBI = { 18 | toNumber: function toNumber(a) { 19 | return typeof a === \\"object\\" ? JSBI.toNumber(a) : Number(a); 20 | }, 21 | add: function add(a, b) { 22 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.add(a, b) : a + b; 23 | }, 24 | subtract: function subtract(a, b) { 25 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.subtract(a, b) : a - b; 26 | }, 27 | multiply: function multiply(a, b) { 28 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.multiply(a, b) : a * b; 29 | }, 30 | divide: function divide(a, b) { 31 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.divide(a, b) : a / b; 32 | }, 33 | remainder: function remainder(a, b) { 34 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.remainder(a, b) : a % b; 35 | }, 36 | exponentiate: function exponentiate(a, b) { 37 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.exponentiate(a, b) : typeof a === \\"bigint\\" && typeof b === \\"bigint\\" ? new Function(\\"a\\", \\"b\\", \\"return a**b\\")(a, b) : Math.pow(a, b); 38 | }, 39 | leftShift: function leftShift(a, b) { 40 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.leftShift(a, b) : a << b; 41 | }, 42 | signedRightShift: function signedRightShift(a, b) { 43 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.signedRightShift(a, b) : a >> b; 44 | }, 45 | bitwiseAnd: function bitwiseAnd(a, b) { 46 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseAnd(a, b) : a & b; 47 | }, 48 | bitwiseOr: function bitwiseOr(a, b) { 49 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseOr(a, b) : a | b; 50 | }, 51 | bitwiseXor: function bitwiseXor(a, b) { 52 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseXor(a, b) : a ^ b; 53 | }, 54 | lessThan: function lessThan(a, b) { 55 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThan(a, b) : a < b; 56 | }, 57 | greaterThan: function greaterThan(a, b) { 58 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThan(a, b) : a > b; 59 | }, 60 | lessThanOrEqual: function lessThanOrEqual(a, b) { 61 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThanOrEqual(a, b) : a <= b; 62 | }, 63 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 64 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 65 | }, 66 | equal: function equal(a, b) { 67 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.equal(a, b) : a === b; 68 | }, 69 | notEqual: function notEqual(a, b) { 70 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.notEqual(a, b) : a !== b; 71 | }, 72 | unaryMinus: function unaryMinus(a) { 73 | return typeof a === \\"object\\" ? JSBI.unaryMinus(a) : -a; 74 | }, 75 | bitwiseNot: function bitwiseNot(a) { 76 | return typeof a === \\"object\\" ? JSBI.bitwiseNot(a) : ~a; 77 | } 78 | }; 79 | import JSBI from \\"./jsbi.mjs\\"; 80 | 81 | function n() { 82 | return 3; 83 | } 84 | 85 | console.log(n() * n()); 86 | 87 | function b() { 88 | return JSBI.BigInt(3); 89 | } 90 | 91 | console.log(JSBI.multiply(b(), b())); 92 | 93 | function nb() { 94 | if (Math.random() < 0.5) { 95 | return 3; 96 | } 97 | 98 | return JSBI.BigInt(3); 99 | } 100 | 101 | console.log(maybeJSBI.multiply(nb(), nb()));" 102 | `; 103 | 104 | exports[`default values 1`] = ` 105 | "var maybeJSBI = { 106 | toNumber: function toNumber(a) { 107 | return typeof a === \\"object\\" ? JSBI.toNumber(a) : Number(a); 108 | }, 109 | add: function add(a, b) { 110 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.add(a, b) : a + b; 111 | }, 112 | subtract: function subtract(a, b) { 113 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.subtract(a, b) : a - b; 114 | }, 115 | multiply: function multiply(a, b) { 116 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.multiply(a, b) : a * b; 117 | }, 118 | divide: function divide(a, b) { 119 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.divide(a, b) : a / b; 120 | }, 121 | remainder: function remainder(a, b) { 122 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.remainder(a, b) : a % b; 123 | }, 124 | exponentiate: function exponentiate(a, b) { 125 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.exponentiate(a, b) : typeof a === \\"bigint\\" && typeof b === \\"bigint\\" ? new Function(\\"a\\", \\"b\\", \\"return a**b\\")(a, b) : Math.pow(a, b); 126 | }, 127 | leftShift: function leftShift(a, b) { 128 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.leftShift(a, b) : a << b; 129 | }, 130 | signedRightShift: function signedRightShift(a, b) { 131 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.signedRightShift(a, b) : a >> b; 132 | }, 133 | bitwiseAnd: function bitwiseAnd(a, b) { 134 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseAnd(a, b) : a & b; 135 | }, 136 | bitwiseOr: function bitwiseOr(a, b) { 137 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseOr(a, b) : a | b; 138 | }, 139 | bitwiseXor: function bitwiseXor(a, b) { 140 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseXor(a, b) : a ^ b; 141 | }, 142 | lessThan: function lessThan(a, b) { 143 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThan(a, b) : a < b; 144 | }, 145 | greaterThan: function greaterThan(a, b) { 146 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThan(a, b) : a > b; 147 | }, 148 | lessThanOrEqual: function lessThanOrEqual(a, b) { 149 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThanOrEqual(a, b) : a <= b; 150 | }, 151 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 152 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 153 | }, 154 | equal: function equal(a, b) { 155 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.equal(a, b) : a === b; 156 | }, 157 | notEqual: function notEqual(a, b) { 158 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.notEqual(a, b) : a !== b; 159 | }, 160 | unaryMinus: function unaryMinus(a) { 161 | return typeof a === \\"object\\" ? JSBI.unaryMinus(a) : -a; 162 | }, 163 | bitwiseNot: function bitwiseNot(a) { 164 | return typeof a === \\"object\\" ? JSBI.bitwiseNot(a) : ~a; 165 | } 166 | }; 167 | import JSBI from \\"./jsbi.mjs\\"; 168 | 169 | function f(y = unknown(), z = maybeJSBI.multiply(y, y)) { 170 | if (!(y instanceof JSBI)) { 171 | throw new RangeError(); 172 | } 173 | 174 | return maybeJSBI.multiply(y, y); 175 | }" 176 | `; 177 | 178 | exports[`destructuring assignment 1`] = ` 179 | "var maybeJSBI = { 180 | toNumber: function toNumber(a) { 181 | return typeof a === \\"object\\" ? JSBI.toNumber(a) : Number(a); 182 | }, 183 | add: function add(a, b) { 184 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.add(a, b) : a + b; 185 | }, 186 | subtract: function subtract(a, b) { 187 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.subtract(a, b) : a - b; 188 | }, 189 | multiply: function multiply(a, b) { 190 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.multiply(a, b) : a * b; 191 | }, 192 | divide: function divide(a, b) { 193 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.divide(a, b) : a / b; 194 | }, 195 | remainder: function remainder(a, b) { 196 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.remainder(a, b) : a % b; 197 | }, 198 | exponentiate: function exponentiate(a, b) { 199 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.exponentiate(a, b) : typeof a === \\"bigint\\" && typeof b === \\"bigint\\" ? new Function(\\"a\\", \\"b\\", \\"return a**b\\")(a, b) : Math.pow(a, b); 200 | }, 201 | leftShift: function leftShift(a, b) { 202 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.leftShift(a, b) : a << b; 203 | }, 204 | signedRightShift: function signedRightShift(a, b) { 205 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.signedRightShift(a, b) : a >> b; 206 | }, 207 | bitwiseAnd: function bitwiseAnd(a, b) { 208 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseAnd(a, b) : a & b; 209 | }, 210 | bitwiseOr: function bitwiseOr(a, b) { 211 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseOr(a, b) : a | b; 212 | }, 213 | bitwiseXor: function bitwiseXor(a, b) { 214 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseXor(a, b) : a ^ b; 215 | }, 216 | lessThan: function lessThan(a, b) { 217 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThan(a, b) : a < b; 218 | }, 219 | greaterThan: function greaterThan(a, b) { 220 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThan(a, b) : a > b; 221 | }, 222 | lessThanOrEqual: function lessThanOrEqual(a, b) { 223 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThanOrEqual(a, b) : a <= b; 224 | }, 225 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 226 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 227 | }, 228 | equal: function equal(a, b) { 229 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.equal(a, b) : a === b; 230 | }, 231 | notEqual: function notEqual(a, b) { 232 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.notEqual(a, b) : a !== b; 233 | }, 234 | unaryMinus: function unaryMinus(a) { 235 | return typeof a === \\"object\\" ? JSBI.unaryMinus(a) : -a; 236 | }, 237 | bitwiseNot: function bitwiseNot(a) { 238 | return typeof a === \\"object\\" ? JSBI.bitwiseNot(a) : ~a; 239 | } 240 | }; 241 | import JSBI from \\"./jsbi.mjs\\"; 242 | 243 | const f = function () { 244 | let [A, B] = [JSBI.BigInt(1), JSBI.BigInt(0)]; 245 | return maybeJSBI.add(A, B); 246 | };" 247 | `; 248 | 249 | exports[`destructuring assignment 2 1`] = ` 250 | "var maybeJSBI = { 251 | toNumber: function toNumber(a) { 252 | return typeof a === \\"object\\" ? JSBI.toNumber(a) : Number(a); 253 | }, 254 | add: function add(a, b) { 255 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.add(a, b) : a + b; 256 | }, 257 | subtract: function subtract(a, b) { 258 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.subtract(a, b) : a - b; 259 | }, 260 | multiply: function multiply(a, b) { 261 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.multiply(a, b) : a * b; 262 | }, 263 | divide: function divide(a, b) { 264 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.divide(a, b) : a / b; 265 | }, 266 | remainder: function remainder(a, b) { 267 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.remainder(a, b) : a % b; 268 | }, 269 | exponentiate: function exponentiate(a, b) { 270 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.exponentiate(a, b) : typeof a === \\"bigint\\" && typeof b === \\"bigint\\" ? new Function(\\"a\\", \\"b\\", \\"return a**b\\")(a, b) : Math.pow(a, b); 271 | }, 272 | leftShift: function leftShift(a, b) { 273 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.leftShift(a, b) : a << b; 274 | }, 275 | signedRightShift: function signedRightShift(a, b) { 276 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.signedRightShift(a, b) : a >> b; 277 | }, 278 | bitwiseAnd: function bitwiseAnd(a, b) { 279 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseAnd(a, b) : a & b; 280 | }, 281 | bitwiseOr: function bitwiseOr(a, b) { 282 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseOr(a, b) : a | b; 283 | }, 284 | bitwiseXor: function bitwiseXor(a, b) { 285 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseXor(a, b) : a ^ b; 286 | }, 287 | lessThan: function lessThan(a, b) { 288 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThan(a, b) : a < b; 289 | }, 290 | greaterThan: function greaterThan(a, b) { 291 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThan(a, b) : a > b; 292 | }, 293 | lessThanOrEqual: function lessThanOrEqual(a, b) { 294 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThanOrEqual(a, b) : a <= b; 295 | }, 296 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 297 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 298 | }, 299 | equal: function equal(a, b) { 300 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.equal(a, b) : a === b; 301 | }, 302 | notEqual: function notEqual(a, b) { 303 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.notEqual(a, b) : a !== b; 304 | }, 305 | unaryMinus: function unaryMinus(a) { 306 | return typeof a === \\"object\\" ? JSBI.unaryMinus(a) : -a; 307 | }, 308 | bitwiseNot: function bitwiseNot(a) { 309 | return typeof a === \\"object\\" ? JSBI.bitwiseNot(a) : ~a; 310 | } 311 | }; 312 | import JSBI from \\"./jsbi.mjs\\"; 313 | 314 | const f = function () { 315 | let A = JSBI.BigInt(1); 316 | let B = JSBI.BigInt(0); 317 | [A, B] = [JSBI.BigInt(3), JSBI.BigInt(4)]; 318 | return maybeJSBI.add(A, B); 319 | };" 320 | `; 321 | 322 | exports[`internal bigint function 1`] = ` 323 | "import JSBI from \\"./jsbi.mjs\\"; 324 | 325 | function f(a) { 326 | return JSBI.multiply(a, a); 327 | } 328 | 329 | console.log(f(JSBI.BigInt(3)));" 330 | `; 331 | 332 | exports[`internal number function 1`] = ` 333 | "import JSBI from \\"./jsbi.mjs\\"; 334 | 335 | function f(a) { 336 | return a * a; 337 | } 338 | 339 | console.log(f(3));" 340 | `; 341 | 342 | exports[`it does not replace expression for a mutable variable 1`] = ` 343 | "import JSBI from \\"./jsbi.mjs\\"; 344 | 345 | function f() { 346 | for (let i = 0; i < 10; i += 1) { 347 | console.log(i * i); 348 | } 349 | }" 350 | `; 351 | 352 | exports[`it does not replace expression for a mutable variable2 1`] = ` 353 | "import JSBI from \\"./jsbi.mjs\\"; 354 | 355 | function f() { 356 | let i = 1; 357 | i = -i; 358 | return i * i; 359 | }" 360 | `; 361 | 362 | exports[`it works with AssignmentExpressions 1`] = ` 363 | "var _x, _x2, _x3, _x4, _y; 364 | 365 | import JSBI from \\"./jsbi.mjs\\"; 366 | const o = {}; 367 | o.x = {}; 368 | o.x.y = JSBI.BigInt(1); 369 | o.x.yz = JSBI.BigInt(1); 370 | const y = 'y'; 371 | const z = 'z'; 372 | const b = JSBI.BigInt(1); 373 | const array = [JSBI.BigInt(1)]; 374 | let i = 1; 375 | _x = o.x, _x.y = JSBI.add(_x.y, b); 376 | _x2 = o.x, _x2['y'] = JSBI.add(_x2['y'], b); 377 | _x3 = o.x, _x3[y] = JSBI.add(_x3[y], b); 378 | _x4 = o.x, _y = y + z, _x4[_y] = JSBI.add(_x4[_y], b); 379 | array[i] = JSBI.add(array[i], b); 380 | array[0] = JSBI.add(array[0], b);" 381 | `; 382 | 383 | exports[`maybeJSBI 1`] = ` 384 | "import JSBI from \\"./jsbi.mjs\\"; 385 | 386 | function f(a) { 387 | return JSBI.toNumber(JSBI.asUintN(64, a)); 388 | } 389 | 390 | console.log(f(JSBI.BigInt(3)));" 391 | `; 392 | 393 | exports[`maybeJSBI avoided for a FunctionExpression 1`] = ` 394 | "import JSBI from \\"./jsbi.mjs\\"; 395 | 396 | const f = function (x) { 397 | if (typeof x !== 'number') { 398 | throw new RangeError(); 399 | } 400 | 401 | return x * x; 402 | };" 403 | `; 404 | 405 | exports[`maybeJSBI avoided for non-constants 1`] = ` 406 | "import JSBI from \\"./jsbi.mjs\\"; 407 | 408 | const f = function (x) { 409 | if (!(x instanceof JSBI)) { 410 | throw new RangeError(); 411 | } 412 | 413 | x = JSBI.multiply(x, x); 414 | return x; 415 | };" 416 | `; 417 | 418 | exports[`maybeJSBI avoided for non-constants 2 1`] = ` 419 | "import JSBI from \\"./jsbi.mjs\\"; 420 | 421 | const f = function (x) { 422 | x = JSBI.BigInt(x); 423 | x = JSBI.multiply(x, x); 424 | return x; 425 | };" 426 | `; 427 | 428 | exports[`maybeJSBI avoided for non-constants 3 1`] = ` 429 | "import JSBI from \\"./jsbi.mjs\\"; 430 | 431 | const f = function (x) { 432 | x = +x; 433 | x = x * x; 434 | return x; 435 | };" 436 | `; 437 | 438 | exports[`maybeJSBI2 1`] = ` 439 | "import JSBI from \\"./jsbi.mjs\\"; 440 | 441 | function f(a) { 442 | return Number(JSBI.lessThan(JSBI.BigInt(a), JSBI.BigInt(0))); 443 | } 444 | 445 | console.log(f(JSBI.BigInt(3)));" 446 | `; 447 | 448 | exports[`maybeJSBI2a 1`] = ` 449 | "import JSBI from \\"./jsbi.mjs\\"; 450 | 451 | function f(a) { 452 | const x = JSBI.lessThan(JSBI.BigInt(a), JSBI.BigInt(0)); 453 | return Number(x); 454 | } 455 | 456 | console.log(f(JSBI.BigInt(3)));" 457 | `; 458 | 459 | exports[`maybeJSBI3 1`] = ` 460 | "import JSBI from \\"./jsbi.mjs\\"; 461 | 462 | function f(a) { 463 | return Math.floor(a + a); 464 | }" 465 | `; 466 | 467 | exports[`non-strict comparisions are not changed 1`] = ` 468 | "import JSBI from \\"./jsbi.mjs\\"; 469 | const g = JSBI.BigInt(1); 470 | 471 | if (g == 1) { 472 | console.log(g); 473 | } 474 | 475 | if (g != 1) { 476 | console.log(g); 477 | } 478 | 479 | if (g < 1) { 480 | console.log(g); 481 | } 482 | 483 | if (g > 1) { 484 | console.log(g); 485 | } 486 | 487 | if (g <= 1) { 488 | console.log(g); 489 | } 490 | 491 | if (g >= 1) { 492 | console.log(g); 493 | }" 494 | `; 495 | 496 | exports[`sometimes type of conditional expression can be determined as JSBI 1`] = ` 497 | "import JSBI from \\"./jsbi.mjs\\"; 498 | 499 | function f(a) { 500 | const b = JSBI.equal(JSBI.remainder(a, JSBI.BigInt(3)), JSBI.BigInt(0)) ? JSBI.BigInt(1) : JSBI.BigInt(3); 501 | return JSBI.multiply(b, b); 502 | } 503 | 504 | export default f;" 505 | `; 506 | 507 | exports[`typeof type guard (see https://www.typescriptlang.org/docs/handbook/2/narrowing.html#typeof-type-guards) 1`] = ` 508 | "var maybeJSBI = { 509 | toNumber: function toNumber(a) { 510 | return typeof a === \\"object\\" ? JSBI.toNumber(a) : Number(a); 511 | }, 512 | add: function add(a, b) { 513 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.add(a, b) : a + b; 514 | }, 515 | subtract: function subtract(a, b) { 516 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.subtract(a, b) : a - b; 517 | }, 518 | multiply: function multiply(a, b) { 519 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.multiply(a, b) : a * b; 520 | }, 521 | divide: function divide(a, b) { 522 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.divide(a, b) : a / b; 523 | }, 524 | remainder: function remainder(a, b) { 525 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.remainder(a, b) : a % b; 526 | }, 527 | exponentiate: function exponentiate(a, b) { 528 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.exponentiate(a, b) : typeof a === \\"bigint\\" && typeof b === \\"bigint\\" ? new Function(\\"a\\", \\"b\\", \\"return a**b\\")(a, b) : Math.pow(a, b); 529 | }, 530 | leftShift: function leftShift(a, b) { 531 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.leftShift(a, b) : a << b; 532 | }, 533 | signedRightShift: function signedRightShift(a, b) { 534 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.signedRightShift(a, b) : a >> b; 535 | }, 536 | bitwiseAnd: function bitwiseAnd(a, b) { 537 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseAnd(a, b) : a & b; 538 | }, 539 | bitwiseOr: function bitwiseOr(a, b) { 540 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseOr(a, b) : a | b; 541 | }, 542 | bitwiseXor: function bitwiseXor(a, b) { 543 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.bitwiseXor(a, b) : a ^ b; 544 | }, 545 | lessThan: function lessThan(a, b) { 546 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThan(a, b) : a < b; 547 | }, 548 | greaterThan: function greaterThan(a, b) { 549 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThan(a, b) : a > b; 550 | }, 551 | lessThanOrEqual: function lessThanOrEqual(a, b) { 552 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.lessThanOrEqual(a, b) : a <= b; 553 | }, 554 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 555 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 556 | }, 557 | equal: function equal(a, b) { 558 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.equal(a, b) : a === b; 559 | }, 560 | notEqual: function notEqual(a, b) { 561 | return typeof a === \\"object\\" && typeof b === \\"object\\" ? JSBI.notEqual(a, b) : a !== b; 562 | }, 563 | unaryMinus: function unaryMinus(a) { 564 | return typeof a === \\"object\\" ? JSBI.unaryMinus(a) : -a; 565 | }, 566 | bitwiseNot: function bitwiseNot(a) { 567 | return typeof a === \\"object\\" ? JSBI.bitwiseNot(a) : ~a; 568 | } 569 | }; 570 | import JSBI from \\"./jsbi.mjs\\"; 571 | 572 | function f1(a) { 573 | if (!(a instanceof JSBI)) { 574 | throw new RangeError(); 575 | } 576 | 577 | return JSBI.multiply(a, a); 578 | } 579 | 580 | function f2(a, b) { 581 | if (!(a instanceof JSBI) || !(b instanceof JSBI)) { 582 | throw new RangeError(); 583 | } 584 | 585 | return JSBI.multiply(a, b); 586 | } 587 | 588 | function f3(a) { 589 | if (typeof a !== 'number') { 590 | throw new RangeError(); 591 | } 592 | 593 | return a * a; 594 | } 595 | 596 | function f4(a, b) { 597 | if (typeof a !== 'number' || typeof b !== 'number') { 598 | throw new RangeError(); 599 | } 600 | 601 | return a * b; 602 | } 603 | 604 | function f5(a) { 605 | if (!(a instanceof JSBI)) { 606 | void 0; 607 | } 608 | 609 | return maybeJSBI.multiply(a, a); 610 | } 611 | 612 | export { f1, f2, f3, f4, f5 };" 613 | `; 614 | 615 | exports[`works 1`] = ` 616 | "import JSBI from \\"./jsbi.mjs\\"; 617 | 618 | function f() { 619 | const x = JSBI.BigInt(1); 620 | return JSBI.add(x, x); 621 | }" 622 | `; 623 | 624 | exports[`works when type of variable is changed 1`] = ` 625 | "import JSBI from \\"./jsbi.mjs\\"; 626 | let g1 = 1; 627 | g1 = JSBI.BigInt(1); 628 | 629 | if (JSBI.equal(g1, JSBI.BigInt(1))) { 630 | console.log(g1); 631 | }" 632 | `; 633 | -------------------------------------------------------------------------------- /__tests__/index-test.js: -------------------------------------------------------------------------------- 1 | // to run this test file use `npx jest` in the parent folder or `npm run test` 2 | // to transform some-file.js use a command: `npx babel --plugins=module:./index.js some-file.js` from the parent folder 3 | 4 | // src/__tests__/index-test.js 5 | const babel = require('@babel/core'); 6 | const plugin = require('../index.js'); 7 | 8 | 9 | it('it works with AssignmentExpressions', function () { 10 | const example = ` 11 | const o = {}; 12 | o.x = {}; 13 | o.x.y = 1n; 14 | o.x.yz = 1n; 15 | const y = 'y'; 16 | const z = 'z'; 17 | const b = 1n; 18 | const array = [1n]; 19 | let i = 1; 20 | 21 | o.x.y += b; 22 | o.x['y'] += b; 23 | o.x[y] += b; 24 | o.x[y + z] += b; 25 | array[i] += b; 26 | array[0] += b; 27 | `; 28 | const {code} = babel.transform(example, {plugins: [plugin]}); 29 | expect(code).toMatchSnapshot(); 30 | }); 31 | 32 | /* 33 | it('it works with UpdateExpression', function () { 34 | const example = ` 35 | const o = {}; 36 | o.x = {}; 37 | o.x.y = 1n; 38 | o.x.yz = 1n; 39 | const y = 'y'; 40 | const z = 'z'; 41 | const b = 1n; 42 | 43 | ++o.x.y; 44 | ++o.x['y']; 45 | ++o.x[y]; 46 | ++o.x[y + z]; 47 | 48 | o.x.y++; 49 | o.x['y']++; 50 | o.x[y]++; 51 | o.x[y + z]++; 52 | `; 53 | const {code} = babel.transform(example, {plugins: [plugin]}); 54 | expect(code).toMatchSnapshot(); 55 | }); 56 | */ 57 | 58 | it('works when type of variable is changed', function () { 59 | const example = ` 60 | let g1 = 1; 61 | g1 = 1n; 62 | if (g1 === 1n) { 63 | console.log(g1); 64 | } 65 | `; 66 | const {code} = babel.transform(example, {plugins: [plugin]}); 67 | expect(code).toMatchSnapshot(); 68 | }); 69 | 70 | it('non-strict comparisions are not changed', function () { 71 | const example = ` 72 | const g = 1n; 73 | if (g == 1) { 74 | console.log(g); 75 | } 76 | if (g != 1) { 77 | console.log(g); 78 | } 79 | if (g < 1) { 80 | console.log(g); 81 | } 82 | if (g > 1) { 83 | console.log(g); 84 | } 85 | if (g <= 1) { 86 | console.log(g); 87 | } 88 | if (g >= 1) { 89 | console.log(g); 90 | } 91 | `; 92 | const {code} = babel.transform(example, {plugins: [plugin]}); 93 | expect(code).toMatchSnapshot(); 94 | }); 95 | 96 | it('BigInt.asUintN(n, a) is replaced', function () { 97 | const example = ` 98 | const g = 1n; 99 | BigInt.asUintN(10, g) 100 | `; 101 | const {code} = babel.transform(example, {plugins: [plugin]}); 102 | expect(code).toMatchSnapshot(); 103 | }); 104 | 105 | it('works', function () { 106 | const example = ` 107 | function f() { 108 | const x = 1n; 109 | return x + x; 110 | } 111 | `; 112 | const {code} = babel.transform(example, {plugins: [plugin]}); 113 | expect(code).toMatchSnapshot(); 114 | }); 115 | 116 | it('BigInt.asUintN(64, a), BigInt.asIntN(64, a)', function () { 117 | const example = ` 118 | const a = 1n; 119 | console.log(BigInt.asUintN(64, a)); 120 | console.log(BigInt.asIntN(64, a)); 121 | `; 122 | const {code} = babel.transform(example, {plugins: [plugin]}); 123 | expect(code).toMatchSnapshot(); 124 | }); 125 | 126 | it('typeof type guard (see https://www.typescriptlang.org/docs/handbook/2/narrowing.html#typeof-type-guards)', function () { 127 | const example = ` 128 | function f1(a) { 129 | if (typeof a !== 'bigint') { 130 | throw new RangeError(); 131 | } 132 | return a * a; 133 | } 134 | function f2(a, b) { 135 | if (typeof a !== 'bigint' || typeof b !== 'bigint') { 136 | throw new RangeError(); 137 | } 138 | return a * b; 139 | } 140 | function f3(a) { 141 | if (typeof a !== 'number') { 142 | throw new RangeError(); 143 | } 144 | return a * a; 145 | } 146 | function f4(a, b) { 147 | if (typeof a !== 'number' || typeof b !== 'number') { 148 | throw new RangeError(); 149 | } 150 | return a * b; 151 | } 152 | function f5(a) { 153 | if (typeof a !== 'bigint') { 154 | void 0; 155 | } 156 | return a * a; 157 | } 158 | export {f1, f2, f3, f4, f5}; 159 | `; 160 | const {code} = babel.transform(example, {plugins: [plugin]}); 161 | expect(code).toMatchSnapshot(); 162 | }); 163 | 164 | it('it does not replace expression for a mutable variable', function () { 165 | const example = ` 166 | function f() { 167 | for (let i = 0; i < 10; i += 1) { 168 | console.log(i * i); 169 | } 170 | } 171 | `; 172 | const {code} = babel.transform(example, {plugins: [plugin]}); 173 | expect(code).toMatchSnapshot(); 174 | }); 175 | 176 | it('it does not replace expression for a mutable variable2', function () { 177 | const example = ` 178 | function f() { 179 | let i = 1; 180 | i = -i; 181 | return i * i; 182 | } 183 | `; 184 | const {code} = babel.transform(example, {plugins: [plugin]}); 185 | expect(code).toMatchSnapshot(); 186 | }); 187 | 188 | 189 | it('default values', function () { 190 | const example = ` 191 | function f(y = unknown(), z = y * y) { 192 | if (typeof y !== 'bigint') { 193 | throw new RangeError(); 194 | } 195 | return y * y; 196 | } 197 | `; 198 | const {code} = babel.transform(example, {plugins: [plugin]}); 199 | expect(code).toMatchSnapshot(); 200 | }); 201 | 202 | 203 | it('arguments in non-strict mode', function () { 204 | const example = ` 205 | function func(a) { 206 | if (typeof a !== 'number') { 207 | throw new RangeError(); 208 | } 209 | arguments[0] = 99n; 210 | console.log(a * a); 211 | } 212 | func(10); // prints 9801 213 | `; 214 | try { 215 | const {code} = babel.transform(example, {plugins: [plugin]}); 216 | expect(code).toMatchSnapshot(); 217 | } catch (error) { 218 | console.assert(error instanceof RangeError); 219 | } 220 | }); 221 | 222 | it('eval in non-strict mode', function () { 223 | const example = ` 224 | function func(a) { 225 | if (typeof a !== 'number') { 226 | throw new RangeError(); 227 | } 228 | eval('a = 99n') 229 | console.log(a * a); 230 | } 231 | func(10); // prints 9801 232 | `; 233 | try { 234 | const {code} = babel.transform(example, {plugins: [plugin]}); 235 | expect(code).toMatchSnapshot(); 236 | } catch (error) { 237 | console.assert(error instanceof RangeError); 238 | } 239 | }); 240 | 241 | 242 | it('sometimes type of conditional expression can be determined as JSBI', function () { 243 | const example = ` 244 | function f(a) { 245 | const b = a % 3n === 0n ? 1n : 3n; 246 | return b * b; 247 | } 248 | export default f; 249 | `; 250 | const {code} = babel.transform(example, {plugins: [plugin]}); 251 | expect(code).toMatchSnapshot(); 252 | }); 253 | 254 | it('internal number function', function () { 255 | const example = ` 256 | function f(a) { 257 | return a * a; 258 | } 259 | console.log(f(3)); 260 | `; 261 | const {code} = babel.transform(example, {plugins: [plugin]}); 262 | expect(code).toMatchSnapshot(); 263 | }); 264 | 265 | it('internal bigint function', function () { 266 | const example = ` 267 | function f(a) { 268 | return a * a; 269 | } 270 | console.log(f(3n)); 271 | `; 272 | const {code} = babel.transform(example, {plugins: [plugin]}); 273 | expect(code).toMatchSnapshot(); 274 | }); 275 | 276 | it('CallExpression\'s type', function () { 277 | const example = ` 278 | function n() { 279 | return 3; 280 | } 281 | console.log(n() * n()); 282 | function b() { 283 | return 3n; 284 | } 285 | console.log(b() * b()); 286 | function nb() { 287 | if (Math.random() < 0.5) { 288 | return 3; 289 | } 290 | return 3n; 291 | } 292 | console.log(nb() * nb()); 293 | `; 294 | const {code} = babel.transform(example, {plugins: [plugin]}); 295 | expect(code).toMatchSnapshot(); 296 | }); 297 | 298 | it('maybeJSBI', function () { 299 | const example = ` 300 | function f(a) { 301 | return Number(BigInt.asUintN(64, a)); 302 | } 303 | console.log(f(3n)); 304 | `; 305 | const {code} = babel.transform(example, {plugins: [plugin]}); 306 | expect(code).toMatchSnapshot(); 307 | }); 308 | it('maybeJSBI2', function () { 309 | const example = ` 310 | function f(a) { 311 | return Number(BigInt(a) < BigInt(0)); 312 | } 313 | console.log(f(3n)); 314 | `; 315 | const {code} = babel.transform(example, {plugins: [plugin]}); 316 | expect(code).toMatchSnapshot(); 317 | }); 318 | it('maybeJSBI2a', function () { 319 | const example = ` 320 | function f(a) { 321 | const x = BigInt(a) < BigInt(0); 322 | return Number(x); 323 | } 324 | console.log(f(3n)); 325 | `; 326 | const {code} = babel.transform(example, {plugins: [plugin]}); 327 | expect(code).toMatchSnapshot(); 328 | }); 329 | it('maybeJSBI3', function () { 330 | const example = ` 331 | function f(a) { 332 | return Math.floor(a + a); 333 | } 334 | `; 335 | const {code} = babel.transform(example, {plugins: [plugin]}); 336 | expect(code).toMatchSnapshot(); 337 | }); 338 | 339 | 340 | it('maybeJSBI avoided for a FunctionExpression', function () { 341 | const example = ` 342 | const f = function (x) { 343 | if (typeof x !== 'number') { 344 | throw new RangeError(); 345 | } 346 | return x * x; 347 | } 348 | `; 349 | const {code} = babel.transform(example, {plugins: [plugin]}); 350 | expect(code).toMatchSnapshot(); 351 | }); 352 | 353 | 354 | it('maybeJSBI avoided for non-constants', function () { 355 | const example = ` 356 | const f = function (x) { 357 | if (typeof x !== 'bigint') { 358 | throw new RangeError(); 359 | } 360 | x = x * x; 361 | return x; 362 | } 363 | `; 364 | const {code} = babel.transform(example, {plugins: [plugin]}); 365 | expect(code).toMatchSnapshot(); 366 | }); 367 | 368 | it('maybeJSBI avoided for non-constants 2', function () { 369 | const example = ` 370 | const f = function (x) { 371 | x = BigInt(x); 372 | x = x * x; 373 | return x; 374 | } 375 | `; 376 | const {code} = babel.transform(example, {plugins: [plugin]}); 377 | expect(code).toMatchSnapshot(); 378 | }); 379 | 380 | it('maybeJSBI avoided for non-constants 3', function () { 381 | const example = ` 382 | const f = function (x) { 383 | x = +x; 384 | x = x * x; 385 | return x; 386 | } 387 | `; 388 | const {code} = babel.transform(example, {plugins: [plugin]}); 389 | expect(code).toMatchSnapshot(); 390 | }); 391 | 392 | 393 | 394 | it('destructuring assignment', function () { 395 | const example = ` 396 | const f = function () { 397 | let [A, B] = [1n, 0n]; 398 | return A + B; 399 | } 400 | `; 401 | const {code} = babel.transform(example, {plugins: [plugin]}); 402 | expect(code).toMatchSnapshot(); 403 | }); 404 | 405 | it('destructuring assignment 2', function () { 406 | const example = ` 407 | const f = function () { 408 | let A = 1n; 409 | let B = 0n; 410 | [A, B] = [3n, 4n]; 411 | return A + B; 412 | } 413 | `; 414 | const {code} = babel.transform(example, {plugins: [plugin]}); 415 | expect(code).toMatchSnapshot(); 416 | }); 417 | 418 | 419 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // see https://github.com/babel/babel/pull/6015 2 | 3 | const syntaxBigInt = require('@babel/plugin-syntax-bigint').default; 4 | 5 | module.exports = function (babel) { 6 | const types = babel.types; 7 | const getFunctionName = function (operator) { 8 | switch (operator) { 9 | // Arithmetic operators 10 | case '+': return 'add'; 11 | case '-': return 'subtract'; 12 | case '*': return 'multiply'; 13 | case '/': return 'divide'; 14 | case '%': return 'remainder'; 15 | case '**': return 'exponentiate'; 16 | // Bitwise shift operators 17 | case '<<': return 'leftShift'; 18 | case '>>': return 'signedRightShift'; 19 | // Binary bitwise operators 20 | case '&': return 'bitwiseAnd'; 21 | case '|': return 'bitwiseOr'; 22 | case '^': return 'bitwiseXor'; 23 | } 24 | return null; 25 | }; 26 | const getRelationalFunctionName = function (operator) { 27 | // Relational operators 28 | switch (operator) { 29 | case '<': return 'lessThan'; 30 | case '>': return 'greaterThan'; 31 | case '<=': return 'lessThanOrEqual'; 32 | case '>=': return 'greaterThanOrEqual'; 33 | case '===': return 'equal'; 34 | case '!==': return 'notEqual'; 35 | } 36 | return null; 37 | }; 38 | const getUnaryFunctionName = function (operator) { 39 | switch (operator) { 40 | case '-': return 'unaryMinus'; 41 | case '~': return 'bitwiseNot'; 42 | } 43 | return null; 44 | }; 45 | const getUpdateFunctionName = function (operator) { 46 | switch (operator) { 47 | case '++': return 'add'; 48 | case '--': return 'subtract'; 49 | } 50 | return null; 51 | }; 52 | 53 | let visited = new Map(); 54 | const canBeBigInt = function (path) { 55 | if (visited.get(path) != null) { 56 | return visited.get(path); 57 | } 58 | visited.set(path, maybeJSBI); 59 | const result = canBeBigIntInternal(path); 60 | //console.debug('canBeBigInt: ' + path.toString() + ', result: ' + result); 61 | if (result === maybeJSBI) { 62 | visited.delete(path); 63 | } else { 64 | visited.set(path, result); 65 | } 66 | return result; 67 | }; 68 | const and = function (a, b) { 69 | if (a === maybeJSBI) { 70 | return b; 71 | } 72 | if (b === maybeJSBI) { 73 | return a; 74 | } 75 | if (a === JSBI && b === JSBI) { 76 | return JSBI; 77 | } 78 | return false; 79 | }; 80 | 81 | const tryType = function (X, binding, path) { 82 | if ((X === false || X === JSBI) && binding.constant) { 83 | return true; 84 | } 85 | if ((X === false || X === JSBI) && !binding.constant) { 86 | let allAssignmentsHaveSameType = true; 87 | for (const path of binding.constantViolations) { 88 | allAssignmentsHaveSameType = allAssignmentsHaveSameType && canBeBigInt(path) === X; 89 | } 90 | if (allAssignmentsHaveSameType) { 91 | return true; 92 | } 93 | if (visited.get(path) === maybeJSBI) { // assume that variable type is the same 94 | const visitedOriginal = new Map(visited.entries()); 95 | visited.set(path, X); 96 | for (const e of visited.entries()) { 97 | if (e[1] === maybeJSBI) { 98 | visited.delete(e[0]); 99 | } 100 | } 101 | let allAssignmentsHaveSameType = true; 102 | for (const path of binding.constantViolations) { 103 | allAssignmentsHaveSameType = allAssignmentsHaveSameType && canBeBigInt(path) === X; 104 | } 105 | if (allAssignmentsHaveSameType) { 106 | return true; 107 | } 108 | visited = visitedOriginal; 109 | } 110 | } 111 | return false; 112 | }; 113 | 114 | const canBeBigIntInternal = function (path) { 115 | if (path.node.type === 'BigIntLiteral') { 116 | return JSBI; 117 | } 118 | if (path.node.type === 'NumericLiteral') { 119 | return false; 120 | } 121 | if (path.node.type === 'StringLiteral') { 122 | return false; 123 | } 124 | if (path.node.type === 'NullLiteral') { 125 | return false; 126 | } 127 | if (path.node.type === 'RegExpLiteral') { 128 | return false; 129 | } 130 | if (path.node.type === 'BooleanLiteral') { 131 | return false; 132 | } 133 | if (path.node.type === 'TemplateLiteral') { 134 | return false; 135 | } 136 | // Math.floor(a / b) 137 | if (path.parentPath.node.type === 'CallExpression' && 138 | path.parentPath.node.arguments.length === 1 && 139 | path.parentPath.node.arguments[0] === path.node) { 140 | const callee = path.parentPath.node.callee; 141 | if (callee.type === 'MemberExpression' && 142 | callee.object.type === 'Identifier' && 143 | callee.object.name === 'Math') { 144 | return false; 145 | } 146 | } 147 | if (path.node.type === 'UnaryExpression') { 148 | if (path.node.operator === '+') { // +0n is not allowed 149 | return false; 150 | } 151 | return canBeBigInt(path.get('argument')); 152 | } 153 | if (path.node.type === 'BinaryExpression') { 154 | if (getRelationalFunctionName(path.node.operator) != null) { 155 | return false; 156 | } 157 | return and(canBeBigInt(path.get('left')), canBeBigInt(path.get('right'))); 158 | } 159 | if (path.node.type === 'AssignmentExpression') { 160 | if (path.node.left.type === 'ArrayPattern') { 161 | return maybeJSBI; 162 | } 163 | if (path.node.operator === '=') { 164 | return canBeBigInt(path.get('right')); 165 | } 166 | return and(canBeBigInt(path.get('left')), canBeBigInt(path.get('right'))); 167 | } 168 | 169 | if (path.node.type === 'Identifier') { 170 | const binding = path.scope.getBinding(path.node.name); 171 | if (binding != null) { 172 | if (binding.path.node.type === 'VariableDeclarator') { 173 | const x = binding.path.get('init'); 174 | if (x.node != null) { 175 | let X = null; 176 | if (x.node.type !== 'ArrayExpression') { 177 | X = canBeBigInt(x); 178 | if (tryType(X, binding, path)) { 179 | return X; 180 | } 181 | } 182 | } 183 | } 184 | for (const path of binding.referencePaths) { 185 | //The next code causes infinite recursion, seems: 186 | //if (path.parentPath.node.type === 'BinaryExpression' && getFunctionName(path.parentPath.node.operator) != null && canBeBigInt(path.parentPath) === false) { 187 | // return false; 188 | //} 189 | } 190 | if (binding.path.node.type === 'Identifier' && binding.path.parentPath.node.type === 'FunctionDeclaration') { 191 | //console.log(binding.path.parentPath.node, '!!!'); 192 | const functionPath = binding.path.parentPath; 193 | const id = functionPath.get('id'); 194 | const functionBinding = functionPath.scope.getBinding(id.node.name); 195 | if (functionBinding != null) { 196 | let argIsBigInt = undefined; 197 | //TODO: check no exports 198 | for (const path of functionBinding.referencePaths) { 199 | //console.log('function call: ' + path.parentPath + '', path.parentPath); 200 | const functionCall = path.parentPath; 201 | if (types.isCallExpression(functionCall)) { 202 | //TODO: check arguments 203 | const args = functionCall.get('arguments'); 204 | for (let i = 0; i < args.length; i += 1) { 205 | const a = args[i]; 206 | //console.log('arg', a); 207 | const t = canBeBigInt(a); 208 | if (t === false && (argIsBigInt == undefined || argIsBigInt === 'false')) { 209 | argIsBigInt = 'false'; 210 | } else if (t === JSBI && (argIsBigInt == undefined || argIsBigInt === 'true')) { 211 | argIsBigInt = 'true'; 212 | } else { 213 | argIsBigInt = 'NO'; 214 | } 215 | } 216 | } else { 217 | argIsBigInt = 'NO'; 218 | } 219 | } 220 | if (argIsBigInt === 'false') { 221 | return false; 222 | } 223 | if (argIsBigInt === 'true') { 224 | return JSBI; 225 | } 226 | } 227 | } 228 | } else { 229 | if (path.node.name === 'undefined') { 230 | return false; 231 | } 232 | } 233 | const checkTypeOf = function (node, variableName, type, not) { 234 | // typeof x === "bigint" 235 | if (node.type === 'BinaryExpression' && (!not ? node.operator === '===' : node.operator === '!==')) { 236 | if (node.left.type === 'UnaryExpression' && node.left.operator === 'typeof') { 237 | if (node.left.argument.type === 'Identifier' && node.left.argument.name === variableName) { 238 | if (node.right.type === 'StringLiteral' && node.right.value === type) { 239 | return true; 240 | } 241 | } 242 | } 243 | } 244 | if (type === 'bigint') { 245 | // x instanceof JSBI 246 | if (!not || node.type === 'UnaryExpression' && node.operator === '!') { 247 | if (node.argument.type === 'BinaryExpression' && node.argument.operator === 'instanceof') { 248 | if (node.argument.left.type === 'Identifier' && node.argument.left.name === variableName) { 249 | if (node.argument.right.type === 'Identifier' && node.argument.right.name === 'JSBI') { 250 | return true; 251 | } 252 | } 253 | } 254 | } 255 | } 256 | return false; 257 | }; 258 | if (binding != null && binding.constant) { 259 | const ifStatement = path.findParent(path => path.isIfStatement()); 260 | const variableName = path.node.name; 261 | if (ifStatement != null) { 262 | const tmp = ifStatement.get('test'); 263 | if (tmp.node.operator === '&&') { 264 | if (checkTypeOf(tmp.node.left, variableName, 'number', false)) { 265 | return false; 266 | } 267 | if (checkTypeOf(tmp.node.right, variableName, 'number', false)) { 268 | return false; 269 | } 270 | } 271 | } 272 | } 273 | if (binding != null) { 274 | //console.debug(binding); 275 | const functionDeclarationOrExpression = path.findParent(path => path.isFunctionDeclaration() || path.isFunctionExpression()); 276 | if (functionDeclarationOrExpression != null && functionDeclarationOrExpression.node.params.filter(param => !types.isIdentifier(param)).length == 0) { 277 | const body = functionDeclarationOrExpression.get('body'); 278 | const x = body.get('body')[0]; 279 | if (types.isIfStatement(x)) { 280 | const ifStatement = x; 281 | const tmp = ifStatement.get('test'); 282 | const variableName = path.node.name; 283 | const consequent = ifStatement.get('consequent').node; 284 | let ok = false; 285 | if (types.isBlockStatement(consequent)) { 286 | if (consequent.body.length === 1) { 287 | if (types.isThrowStatement(consequent.body[0])) { 288 | ok = true; 289 | } 290 | } 291 | } 292 | const isNotTypeOfCheck = function (node, type, variableName) { 293 | if (checkTypeOf(node, variableName, type, true)) { 294 | return true; 295 | } 296 | if (node.type === 'LogicalExpression' && node.operator === '||') { 297 | if (isNotTypeOfCheck(node.left, type, variableName)) { 298 | return true; 299 | } 300 | if (isNotTypeOfCheck(node.right, type, variableName)) { 301 | return true; 302 | } 303 | } 304 | return false; 305 | }; 306 | if (ok && isNotTypeOfCheck(tmp.node, 'bigint', variableName)) { 307 | if (tryType(JSBI, binding, path)) { 308 | return JSBI; 309 | } 310 | } 311 | if (ok && isNotTypeOfCheck(tmp.node, 'number', variableName)) { 312 | if (tryType(false, binding, path)) { 313 | return false; 314 | } 315 | } 316 | } 317 | } 318 | } 319 | if (binding != null && !binding.constant) { 320 | let hasFalse = false; 321 | let hasJSBI= false; 322 | for (const path of binding.constantViolations) { 323 | if (canBeBigInt(path) === false) { 324 | hasFalse = true; 325 | } else if (canBeBigInt(path) === JSBI) { 326 | hasJSBI = true; 327 | } 328 | } 329 | if (hasFalse && !hasJSBI) { 330 | if (tryType(false, binding, path)) { 331 | return false; 332 | } 333 | } 334 | if (!hasFalse && hasJSBI) { 335 | if (tryType(JSBI, binding, path)) { 336 | return JSBI; 337 | } 338 | } 339 | } 340 | return maybeJSBI; 341 | } 342 | 343 | if (path.node.type === 'ConditionalExpression') { 344 | const a = canBeBigInt(path.get('consequent')); 345 | const b = canBeBigInt(path.get('alternate')); 346 | return a === b ? a : maybeJSBI; 347 | } 348 | if (path.node.type === 'FunctionExpression') { 349 | return false; 350 | } 351 | if (path.node.type === 'NewExpression') { 352 | return false; 353 | } 354 | if (path.node.type === 'LogicalExpression') { 355 | return false;//? 356 | } 357 | if (path.node.type === 'ObjectProperty') { 358 | return false;//? 359 | } 360 | if (path.node.type === 'CallExpression') { 361 | if (path.node.callee.type === 'MemberExpression' && 362 | path.node.callee.object.type === 'Identifier' && 363 | path.node.callee.object.name === 'Math') { 364 | return false; 365 | } 366 | if (path.node.callee.type === 'Identifier') { 367 | const name = path.node.callee.name; 368 | if (name === 'Number') { 369 | return false; 370 | } 371 | if (name === 'String') { 372 | return false; 373 | } 374 | if (name === 'Boolean') { 375 | return false; 376 | } 377 | if (name === 'BigInt') { 378 | return JSBI; 379 | } 380 | } 381 | if (path.node.callee.type === 'MemberExpression' && 382 | path.node.callee.object.type === 'Identifier' && 383 | (path.node.callee.object.name === 'JSBI' || path.node.callee.object.name === 'BigInt')) { 384 | if (path.node.callee.object.name === 'JSBI') { 385 | if (['lessThan', 'greateThan', 'equal', 'notEqual', 'lessThanOrEqual', 'greaterThanOrEqual', 'toNumber'].indexOf(path.node.callee.property.name) !== -1) { 386 | return false; 387 | } 388 | } 389 | return JSBI; 390 | } 391 | } 392 | if (path.node.type === 'CallExpression') { 393 | if (path.node.callee.type === 'Identifier') { 394 | const binding = path.scope.getBinding(path.node.callee.name); 395 | if (binding != null) { 396 | if (binding.path.node.type === 'FunctionDeclaration' || binding.path.node.type === 'FunctionExpression') { 397 | //console.log('binding.path', binding.path); 398 | //const statements = binding.path.get('body').get('body'); 399 | const statements = []; 400 | binding.path.getScope().traverse(binding.path.node, {ReturnStatement: function(path){ statements.push(path); }}, this); 401 | let returnType = undefined; 402 | for (const statement of statements) { 403 | if (statement.type === 'ReturnStatement') { 404 | const t = canBeBigInt(statement.get('argument')); 405 | if (returnType === undefined) { 406 | returnType = t; 407 | } 408 | if (returnType !== t) { 409 | returnType = maybeJSBI; 410 | } 411 | } 412 | } 413 | if (returnType === false || returnType == JSBI) { 414 | return returnType; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | if (path.node.type === 'CallExpression') { 421 | return maybeJSBI; 422 | } 423 | if (path.node.type === 'UpdateExpression') { 424 | return canBeBigInt(path.get('argument')); 425 | } 426 | if (path.node.type === 'MemberExpression') { 427 | return maybeJSBI; 428 | } 429 | if (path.node.type === 'ObjectExpression') { 430 | return false; 431 | } 432 | if (path.node.type === 'ArrayExpression') { 433 | return false; 434 | } 435 | if (path.node.type === 'ArrayPattern') { 436 | return maybeJSBI; 437 | } 438 | console.warn('unknown path.node.type: ' + path.node.type); 439 | //TODO: 440 | return maybeJSBI; 441 | }; 442 | 443 | const JSBI = 'JSBI'; 444 | const maybeJSBI = 'maybeJSBI'; 445 | //const maybeJSBI = JSBI; 446 | const IMPORT_PATH = './jsbi.mjs'; 447 | 448 | const maybeJSBICode = ` 449 | 450 | var maybeJSBI = { 451 | toNumber: function toNumber(a) { 452 | return typeof a === "object" ? JSBI.toNumber(a) : Number(a); 453 | }, 454 | add: function add(a, b) { 455 | return typeof a === "object" && typeof b === "object" ? JSBI.add(a, b) : a + b; 456 | }, 457 | subtract: function subtract(a, b) { 458 | return typeof a === "object" && typeof b === "object" ? JSBI.subtract(a, b) : a - b; 459 | }, 460 | multiply: function multiply(a, b) { 461 | return typeof a === "object" && typeof b === "object" ? JSBI.multiply(a, b) : a * b; 462 | }, 463 | divide: function divide(a, b) { 464 | return typeof a === "object" && typeof b === "object" ? JSBI.divide(a, b) : a / b; 465 | }, 466 | remainder: function remainder(a, b) { 467 | return typeof a === "object" && typeof b === "object" ? JSBI.remainder(a, b) : a % b; 468 | }, 469 | exponentiate: function exponentiate(a, b) { 470 | return typeof a === "object" && typeof b === "object" ? JSBI.exponentiate(a, b) : (typeof a === "bigint" && typeof b === "bigint" ? new Function("a", "b", "return a**b")(a, b) : Math.pow(a, b)); 471 | }, 472 | leftShift: function leftShift(a, b) { 473 | return typeof a === "object" && typeof b === "object" ? JSBI.leftShift(a, b) : a << b; 474 | }, 475 | signedRightShift: function signedRightShift(a, b) { 476 | return typeof a === "object" && typeof b === "object" ? JSBI.signedRightShift(a, b) : a >> b; 477 | }, 478 | bitwiseAnd: function bitwiseAnd(a, b) { 479 | return typeof a === "object" && typeof b === "object" ? JSBI.bitwiseAnd(a, b) : a & b; 480 | }, 481 | bitwiseOr: function bitwiseOr(a, b) { 482 | return typeof a === "object" && typeof b === "object" ? JSBI.bitwiseOr(a, b) : a | b; 483 | }, 484 | bitwiseXor: function bitwiseXor(a, b) { 485 | return typeof a === "object" && typeof b === "object" ? JSBI.bitwiseXor(a, b) : a ^ b; 486 | }, 487 | lessThan: function lessThan(a, b) { 488 | return typeof a === "object" && typeof b === "object" ? JSBI.lessThan(a, b) : a < b; 489 | }, 490 | greaterThan: function greaterThan(a, b) { 491 | return typeof a === "object" && typeof b === "object" ? JSBI.greaterThan(a, b) : a > b; 492 | }, 493 | lessThanOrEqual: function lessThanOrEqual(a, b) { 494 | return typeof a === "object" && typeof b === "object" ? JSBI.lessThanOrEqual(a, b) : a <= b; 495 | }, 496 | greaterThanOrEqual: function greaterThanOrEqual(a, b) { 497 | return typeof a === "object" && typeof b === "object" ? JSBI.greaterThanOrEqual(a, b) : a >= b; 498 | }, 499 | equal: function equal(a, b) { 500 | return typeof a === "object" && typeof b === "object" ? JSBI.equal(a, b) : a === b; 501 | }, 502 | notEqual: function notEqual(a, b) { 503 | return typeof a === "object" && typeof b === "object" ? JSBI.notEqual(a, b) : a !== b; 504 | }, 505 | unaryMinus: function unaryMinus(a) { 506 | return typeof a === "object" ? JSBI.unaryMinus(a) : -a; 507 | }, 508 | bitwiseNot: function bitwiseNot(a) { 509 | return typeof a === "object" ? JSBI.bitwiseNot(a) : ~a; 510 | } 511 | }; 512 | `; 513 | //const maybeJSBICode = ''; 514 | 515 | const typeOfIgnore = new Set(); 516 | 517 | return { 518 | inherits: syntaxBigInt, 519 | visitor: { 520 | CallExpression: function (path, state) { 521 | if (path.node.callee.name === 'Number') { 522 | const JSBI = canBeBigInt(path.get('arguments')[0]); 523 | if (JSBI !== false) { 524 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier('toNumber')), path.node.arguments)); 525 | } 526 | } 527 | if (path.node.callee.name === 'BigInt') { 528 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier('BigInt')), path.node.arguments)); 529 | } 530 | if (path.node.callee.type === 'MemberExpression' && 531 | path.node.callee.object.type === 'Identifier' && 532 | path.node.callee.object.name === 'BigInt' && 533 | (path.node.callee.property.name === 'asUintN' || path.node.callee.property.name === 'asIntN')) { 534 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(path.node.callee.property.name)), path.node.arguments)); 535 | } 536 | }, 537 | BigIntLiteral: function (path, state) { 538 | const value = path.node.value; 539 | const number = Number(value); //TODO: 540 | if (number >= Number.MIN_SAFE_INTEGER && number <= Number.MAX_SAFE_INTEGER) { 541 | // 1n -> JSBI.BigInt(1) 542 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier('BigInt')), [types.numericLiteral(number)])); 543 | } else { 544 | // 9007199254740993n -> JSBI.BigInt('9007199254740993') 545 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier('BigInt')), [types.StringLiteral(value)])); 546 | } 547 | }, 548 | BinaryExpression: function (path, state) { 549 | const operator = path.node.operator; 550 | const JSBI = getRelationalFunctionName(operator) != null ? and(canBeBigInt(path.get('left')), canBeBigInt(path.get('right'))) : canBeBigInt(path); 551 | if (JSBI !== false) { 552 | const functionName = getFunctionName(operator) || getRelationalFunctionName(operator); 553 | if (functionName != null) { 554 | // x * y -> JSBI.multiply(x, y) 555 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [path.node.left, path.node.right])); 556 | } 557 | } 558 | // typeof x 559 | if ((operator === '===' || operator === '!==') && 560 | types.isUnaryExpression(path.node.left) && path.node.left.operator === 'typeof' && types.isIdentifier(path.node.left.argument) && 561 | types.isStringLiteral(path.node.right)) { 562 | // typeof x === 'bigint' -> x instanceof JSBI 563 | const typeOfTest = path.node.left; 564 | typeOfIgnore.add(typeOfTest); 565 | if (path.node.right.value === 'bigint') { 566 | const instanceOfNode = types.binaryExpression('instanceof', path.node.left.argument, types.identifier('JSBI')); 567 | path.replaceWith(operator === '!==' ? types.unaryExpression('!', instanceOfNode) : instanceOfNode); 568 | } 569 | } 570 | }, 571 | UnaryExpression: function (path, state) { 572 | const JSBI = canBeBigInt(path); 573 | if (JSBI !== false) { 574 | const functionName = getUnaryFunctionName(path.node.operator); 575 | if (functionName !== null) { 576 | // -x -> JSBI.unaryMinus(x) 577 | path.replaceWith(types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [path.node.argument])); 578 | } 579 | } 580 | // typeof x 581 | if (path.node.operator === 'typeof' && !typeOfIgnore.has(path.node)) { 582 | throw new RangeError('not supported'); 583 | } 584 | }, 585 | UpdateExpression: function (path, state) { 586 | throw new RangeError('UpdateExpressions are not supported because of the complexity: ' + path); 587 | // The implementation below is buggy, as it converts ++x to x += 1n even for number x 588 | /* 589 | const JSBI = canBeBigInt(path); 590 | if (JSBI !== false) { 591 | const operator = path.node.operator; 592 | const prefix = path.node.prefix; 593 | const functionName = getUpdateFunctionName(operator); 594 | if (functionName != null) { 595 | const one = types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier('BigInt')), [types.numericLiteral(1)]); 596 | const argument = path.node.argument; 597 | if (types.isMemberExpression(argument)) { 598 | if (prefix) { 599 | const x = path.scope.generateUidIdentifier('x'); 600 | path.scope.push({id: x}); 601 | const y = path.scope.generateUidIdentifier('y'); 602 | path.scope.push({id: y}); 603 | // ++object[property] -> (x = object, y = property, x[y] = x[y] + 1) 604 | path.replaceWith(types.sequenceExpression([ 605 | types.assignmentExpression('=', x, argument.object), 606 | types.assignmentExpression('=', y, argument.computed ? argument.property : types.StringLiteral(argument.property.name)), 607 | types.assignmentExpression('=', types.memberExpression(x, y, true), types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [types.memberExpression(x, y, true), one])) 608 | ])); 609 | } else { 610 | const x = path.scope.generateUidIdentifier('x'); 611 | path.scope.push({id: x}); 612 | const y = path.scope.generateUidIdentifier('y'); 613 | path.scope.push({id: y}); 614 | const z = path.scope.generateUidIdentifier('z'); 615 | path.scope.push({id: z}); 616 | // object[property]++ -> (x = object, y = property, z = x[y], x[y] = x[y] + 1, z) 617 | path.replaceWith(types.sequenceExpression([ 618 | types.assignmentExpression('=', x, argument.object), 619 | types.assignmentExpression('=', y, argument.computed ? argument.property : types.StringLiteral(argument.property.name)), 620 | types.assignmentExpression('=', z, types.memberExpression(x, y, true)), 621 | types.assignmentExpression('=', types.memberExpression(x, y, true), types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [z, one])), 622 | z 623 | ])); 624 | } 625 | } else { 626 | if (prefix) { 627 | // ++argument -> (argument = argument + 1) 628 | path.replaceWith(types.assignmentExpression('=', argument, types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [argument, one]))); 629 | } else { 630 | const x = path.scope.generateUidIdentifier('x'); 631 | path.scope.push({id: x}); 632 | // argument++ -> (x = argument, argument = argument + 1, x) 633 | path.replaceWith(types.sequenceExpression([ 634 | types.assignmentExpression('=', x, argument), 635 | types.assignmentExpression('=', argument, types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [argument, one])), 636 | x 637 | ])); 638 | } 639 | } 640 | } 641 | }*/ 642 | 643 | }, 644 | AssignmentExpression: function (path, state) { 645 | const isConstant = function (path) { 646 | if (types.isStringLiteral(path.node)) { 647 | return true; 648 | } 649 | if (types.isNumericLiteral(path.node)) { 650 | return true; 651 | } 652 | if (types.isIdentifier(path.node)) { 653 | const binding = path.scope.getBinding(path.node.name); 654 | if (binding == null) { 655 | console.warn('unknown identifier: ' + path.node.name); 656 | return false; 657 | } 658 | return binding.constant; 659 | } 660 | return false; 661 | }; 662 | if (types.isMemberExpression(path.node.left) && types.isIdentifier(path.node.left.object) && path.node.left.object.name === 'arguments') { 663 | throw new RangeError('arguments should not be used'); 664 | } 665 | const JSBI = canBeBigInt(path); 666 | if (JSBI !== false) { 667 | const operator = path.node.operator; 668 | if (operator.endsWith('=')) { 669 | const functionName = getFunctionName(operator.slice(0, -'='.length)); 670 | if (functionName != null) { 671 | const left = path.node.left; 672 | const right = path.node.right; 673 | if (types.isMemberExpression(left)) { 674 | // object[property] += right -> (x = object, y = property, x[y] = x[y] + right) 675 | const expressions = []; 676 | let x = left.object; 677 | if (!isConstant(path.get('left').get('object'))) { 678 | x = path.scope.generateUidIdentifier('x'); 679 | path.scope.push({id: x}); 680 | expressions.push(types.assignmentExpression('=', x, left.object)); 681 | } 682 | let y = left.property; 683 | if (!isConstant(path.get('left').get('property'))) { 684 | y = path.scope.generateUidIdentifier('y'); 685 | path.scope.push({id: y}); 686 | expressions.push(types.assignmentExpression('=', y, left.property)); 687 | } 688 | const assignment = types.assignmentExpression('=', 689 | types.memberExpression(x, y, left.computed), 690 | types.callExpression( 691 | types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), 692 | [types.memberExpression(x, y, left.computed), right] 693 | ) 694 | ); 695 | expressions.push(assignment); 696 | if (expressions.length === 1) { 697 | path.replaceWith(expressions[0]); 698 | } else { 699 | path.replaceWith(types.sequenceExpression(expressions)); 700 | } 701 | } else { 702 | // left += right -> (left = left + right) 703 | path.replaceWith(types.assignmentExpression('=', left, types.callExpression(types.memberExpression(types.identifier(JSBI), types.identifier(functionName)), [left, right]))); 704 | } 705 | } 706 | } 707 | } 708 | }, 709 | Program: function (path) { 710 | // https://stackoverflow.com/a/35994497 711 | const identifier = types.identifier(JSBI); 712 | const importDefaultSpecifier = types.importDefaultSpecifier(identifier); 713 | const importDeclaration = types.importDeclaration([importDefaultSpecifier], types.stringLiteral(IMPORT_PATH)); 714 | path.unshiftContainer('body', importDeclaration); 715 | }, 716 | Identifier: function (path) { 717 | if (path.node.name === 'eval') { 718 | throw new RangeError('eval should not be used'); 719 | } 720 | } 721 | }, 722 | pre: function () { 723 | visited.clear(); 724 | typeOfIgnore.clear(); 725 | }, 726 | post: function (state) { 727 | //console.log(state); 728 | const usesMaybeJSBI = state.path.toString().indexOf('maybeJSBI') !== -1; 729 | if (usesMaybeJSBI) { 730 | state.ast.program.body.unshift(babel.parse(maybeJSBICode).program.body[0]); 731 | } 732 | } 733 | }; 734 | }; 735 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-bigint", 3 | "version": "1.0.34", 4 | "description": "A plugin for babel to transform `x * y` into something like `JSBI.multiply(x, y)` to support bigints.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/plugin-syntax-bigint": "^7.8.3" 13 | }, 14 | "keywords": [ 15 | "babel-plugin", 16 | "bigint", 17 | "jsbi" 18 | ], 19 | "bugs": { 20 | "url": "https://github.com/Yaffle/babel-plugin-transform-bigint/issues" 21 | }, 22 | "homepage": "https://github.com/Yaffle/babel-plugin-transform-bigint#readme", 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/Yaffle/babel-plugin-transform-bigint.git" 26 | }, 27 | "devDependencies": { 28 | "@babel/cli": "^7.17.10", 29 | "@babel/core": "^7.18.0", 30 | "jest": "^28.1.0" 31 | } 32 | } 33 | --------------------------------------------------------------------------------