Conversation
|
Thanks for posting your current work, nice stuff! As a general thing you'll want to do some of the things in https://github.com/babel/babel/wiki/Adding-a-new-Proposal-to-Babel (generator to print it out, babel types to create a bigint) |
| visitor: { | ||
| Program: { | ||
| exit(path: NodePath, state: Object) { | ||
| state.file.addImport("bigint-polyfill", "default", "BigInt"); |
There was a problem hiding this comment.
This is fine for now, we also use this for transform-runtime. I wouldn't call it a "polyfill" if you are just going to use the variable (then it's just the same as transform-runtime) rather than babel-polyfill.
| t.ExpressionStatement( | ||
| t.CallExpression( | ||
| t.MemberExpression( | ||
| t.CallExpression(t.Identifier("_BigInt"), [ |
There was a problem hiding this comment.
so here is where you can use an API method. This hardcodes it so if someone names another variable _BigInt this would have problems (unlikely but possible)
can use something like path.scope.generateUidIdentifier("BigInt"); But you want it to be the same variable as the import so don't need this actually.
EDIT: addImport actually returns the id
babel/packages/babel-core/src/transformation/file/index.js
Lines 181 to 212 in 28ae47a
| if ( | ||
| path.node.left.type === "BigIntLiteral" && | ||
| path.node.left.type === "BigIntLiteral" | ||
| ) { |
There was a problem hiding this comment.
Such conditionals will not generalize sufficiently. You need to be able to handle code like
function add(a, b) {
return a + b;
}Functions like this are genuinely generic. The logic needs to be, instead, "whenever I see any infix operator, replace it by a function call to my polyfill library". This applies for + as well as === as well as |--they all need to implement the new dispatch algorithm.
I'd recommend that these expand into function calls, rather than method calls, since they may be called with any object or value as the first argument, not just BigInts.
There was a problem hiding this comment.
Just complementing @littledan, we probably don't have path.node.*.type when one of the operands isn't a literal, so we need to have a check at runtime in all BigInt compatible BinaryExpressions if the kind of this operand is a BigInt or not.
Also IMHO, expanding into function calls will make our life easier to follow the spec and implement runtime errors due to invalid operand, such as 10n + 1.
There was a problem hiding this comment.
Actually not that bad then since you don't need the if statement anymore? You just replace + with .add and so on?
There was a problem hiding this comment.
Following Dan's example, what I have in mind is something like that:
function add(a, b) {
var tmp;
if (a instanceof _BingInt || b instanceof _BingInt)
tmp = _BigIntAdd(a, b)
else
tmp = a + b;
return tmp;
}
PS.: The names are just placeholders
There was a problem hiding this comment.
This might also not match the spec semantics. It's important to throw on Number + BigInt, cast to string on String + BigInt, ext.
There was a problem hiding this comment.
Sorry if I wasn't 100% clear. I'm thinking in handle semantic cases and errors into _BigIntAdd(a, b). This function follows the implementation of https://tc39.github.io/proposal-bigint/#sec-additive-operators in JS. Does it make sense?
@hzoo probably we would like to have the "ifs" there to avoid checks at runtime of literals, for example.
| @@ -0,0 +1,2 @@ | |||
| import _BigInt from "bigint-polyfill"; | |||
There was a problem hiding this comment.
As for the polyfill library, I'd suggest starting with something like node-bigint. This has all the functions implemented, though it only works on Node, not the web. I don't think this is a problem, as the Babel implementation is more for exploration/demonstration purposes than actual deployment.
On top of whatever base library you start with, there will have to be significant things to match the form of the spec. This includes:
- Dispatching appropriately on Number vs BigInt
- Methods like
asUintN, which you're unlikely to find elsewhere - Being a little creative with some functions, for example
BigInt.prototype.valueOfshould throw, to prevent implicit coercion to Number.
There was a problem hiding this comment.
would be nice if we could use in the REPL but I guess that's not possible in this case (or we have to fake it)
There was a problem hiding this comment.
I had been thinking of this for the polyfill so that we could get browser support as well https://github.com/MikeMcl/bignumber.js/ @littledan are there any drawbacks? i'm not really familiar with the different libraries in this space
There was a problem hiding this comment.
Could even have an option for diff library if you wanted, if it passes the tests we make it's all good
There was a problem hiding this comment.
@wdhorton Sure, go for it. That library may be slower, that's all, but working in browsers wouldn't be bad.
|
@hzoo @littledan made changes based on your feedback. I've got the addition case passing for A few more questions:
BigInt('1223456')in addition to the literal form
(function(left, right) {
if (left instanceof BigInt && right instanceof BigInt) {
return left.plus(right);
} else {
return left + right;
}
})was transformed to this: function _checkBinaryExpression(left, right) {
if (left instanceof BigInt && right instanceof BigInt) {
return left.plus(right);
} else {
return _checkBinaryExpression(left, right);
}
}which caused infinite recursion on non-BigInt numbers. I might be missing something, but it seems like I can't include So for now I just stuck it in another package ( |
| @@ -0,0 +1,8 @@ | |||
| export default function(left, right) { | |||
| // eslint-disable-next-line no-undef | |||
| if (left instanceof BigInt && right instanceof BigInt) { | |||
There was a problem hiding this comment.
Would it make sense to use window.BigInt (or global.BigInt) to avoid the no-undef rule?
There was a problem hiding this comment.
yes, that makes sense. if I use global.BigInt, will it work in the browser and Node?
There was a problem hiding this comment.
Yes sure, I was thinking about how it would be bundled but that's an unnecessary change anyway.
| if (left instanceof BigInt && right instanceof BigInt) { | ||
| return left.plus(right); | ||
| } else { | ||
| return left + right; |
There was a problem hiding this comment.
I think it's not spec compliant. You need to emit TypeError if Type(left) is different of Type(right). Check the semantics here https://tc39.github.io/proposal-bigint/#sec-additive-operators
In your current function, if left instanceof BigInt === true and left instanceof BigInt === false it will fallback to else path and probably will result wrong result.
| @@ -0,0 +1 @@ | |||
| assert.equal((1000n + 200n).toString(), (new BigInt('1200')).toString()) | |||
There was a problem hiding this comment.
Add test cases when you have BigInt + other primitive.
packages/babel-polyfill/src/index.js
Outdated
|
|
||
| import "core-js/shim"; | ||
| import "regenerator-runtime/runtime"; | ||
| import "./bigint/bigint"; |
There was a problem hiding this comment.
I don't think we want to add this to babel-polyfill itself, since most people won't be using it and will ask about the size increase.
I guess it would be covered by preset-env and only adding if you use the preset/plugin but not sure how that would work really
There was a problem hiding this comment.
fair point. where should i put it?
| } | ||
|
|
||
| if (isString(left) || isString(right)) { | ||
| return left.toString() + right.toString(); |
There was a problem hiding this comment.
This is not quite right. For example, a user may monkey-patch Number.prototype.toString, but that behavior shouldn't be reflected here. Instead, do simply left + right.
In general: If you can try to follow the spec in order, one by one, you'll arrive at something closer to compliance. Spec for +.
| return x instanceof global.BigInt; | ||
| } | ||
|
|
||
| export default function(left, right, operator) { |
There was a problem hiding this comment.
I'm not sure why you take the operator as an argument, but then you just use +.
| } | ||
|
|
||
| if (isBigInt(left) && isBigInt(right)) { | ||
| return left.plus(right); |
There was a problem hiding this comment.
Better to not use a method called plus but instead a function. The problem with a method is that it's easily accessible to users (who will be missing it in the real version). It's better to use a function, which we could refrain from exporting to them.
| @@ -0,0 +1,26 @@ | |||
| import isNumber from "lodash/isNumber"; | |||
| import isString from "lodash/isString"; | |||
There was a problem hiding this comment.
I think we can void the lodash dependency here, you can replace it with:
typeof x != "number" and typeof x != "string".
|
I'm wondering, how are you running this code? Do you have a BigInt library that you are using? Or are you just examining the generated output? |
|
@littledan i just shuffled stuff around a bit, but I made a |
…and concatting on BigInt + String
…ferent bigint lib.
|
@hzoo I'm seeing this error: |
|
if someone is interested in ... - https://github.com/Yaffle/babel-plugin-transform-bigint |
|
@Yaffle that looks great. Have you tried to run it against the tests here? Would you mind helping us pushing this PR forward? |
|
@xtuc, thanks, I didn't try to run it against tests. How can I help you with this PR? |
|
@Yaffle Great work! At a skim, this repo looks generally correct. I'd like to argue that this transform should be kept out of all presets unless explicitly included, as it makes fairly little sense to run in production with the general performance degradation that it causes. |
| @@ -0,0 +1,13 @@ | |||
| { | |||
| "name": "babel-plugin-syntax-bigint", | |||
There was a problem hiding this comment.
The plugin-syntax-bigint is already on master, could we remove it from this PR?
|
@wdhorton Seems like this PR was abandoned. Is there a specific reason? any alternatives? |

I'm working on the transform for BigInt, and I wanted to put up a PR to get some feedback on the direction it's going.
This just works for adding two BigInts right now, obviously there are other operations, errors to be caught, edge cases, etc., etc. but I think if we can nail down the details of this case I should be able to move forward pretty quickly.
Main questions:
For @littledan:
.addmethod until we get real support in the language, since we can't override the+operator in JS)For babel people (@hzoo or others):
When I want to have the polyfill in the transformed file, should I be using
importorrequire? I went withimportbecause there's anaddImportmethod and there doesn't seem to be an analogousstate.file.addRequiremethod, but that breaks my tests unless I usetransform-es2015-modules-commonjsas an additional plugin.Should I be importing/requiring the
BigIntpolyfill as a variable, or should I be putting it on the global as a side effect of the import/require? How would that work if people use theBigIntconstructor themselves in the code they want to transform?Where should the code for the polyfill live? It's going to be a thin wrapper around an existing BigNumber library. Right now "bigint-polyfill" is just a placeholder name. Should it be in
babel-helpers?babel-polyfill? A separate NPM package (that seems less than ideal for development purposes).Any additional feedback on whether or not I'm on the right track with this would be much appreciated.