-
Notifications
You must be signed in to change notification settings - Fork 990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix BSER for nodejs #393
Fix BSER for nodejs #393
Conversation
This fixes the MAX_INT16 and MAX_INT32 constant to be correct and makes numbers without fraction (1 or 1.0) to be encoded as ints Also changes the return values when reading an int64 to be a primitive number if possible. This is a breaking change.
Wow, this is little embarrassing... thanks for taking this on! I think I'm fine with the idea of this making a breaking API change so long as it doesn't bite anyone in practice. Floats a relatively rare in the FB usage of watchman and BSER, so I don't anticipate that that will hurt any of the commonly used tools here. cc: @mostafaeweda for Nuclide and js-fu on the FB side. I wonder if this might explain some of that weird BSER errors you mentioned a while back? Do you anticipate that this will break Nuclide? Would you mind also reviewing the JS here; clearly my implementation was off in some important details :-p |
(CI may be a bit flaky at the moment because of some pretty major changes in the core that haven't been checked out on all platforms) |
@@ -120,6 +120,9 @@ Accumulator.prototype.peekInt = function(size) { | |||
|
|||
Accumulator.prototype.readInt = function(bytes) { | |||
var ival = this.peekInt(bytes); | |||
if (ival instanceof Int64 && isFinite(ival.valueOf())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when is is expected to not be finite?
In that case, the return value here can either be number
or Int64
I'd rather check the isFinite
on the consumer side of this function's return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when we decode bser
and it contains an 64bit integer, we always create an instance of Int64
for it in peekInt()
. For up to 2^53 − 1
javascript engines could display and handle numbers just fine, because the engines use floating point representations internally. For these numbers valueOf()
will return the number, for everything above and up to MAX_INT64 ( 2^63-1
) it will return Infinity
.
So what we do here is simply checking if it is still a number that the javascript-engine can handle and if yes use the number instead of the Int64
Instance. readInt
returns anyway either number
or Int64
even without my change, depending if the integer is 8/16/32bit (number
) or 64bit (Int64
)
Although especially in javascript it does not make sense to return Int64
instances to consumers of bser imho. Because either I try to convert it to number anyway when I receive the Int64
instance because I want to use it for calculations or if I can't convert it because it is to high and javascript cannot handle it natively the only thing I can do with Int64
is forward it to watchman/bser itself again or use it as string, as calculations cannot be done with Int64
instances.
But yes up to you.
@@ -120,6 +120,9 @@ Accumulator.prototype.peekInt = function(size) { | |||
|
|||
Accumulator.prototype.readInt = function(bytes) { | |||
var ival = this.peekInt(bytes); | |||
if (ival instanceof Int64 && isFinite(ival.valueOf())) { | |||
ival = ival.valueOf(); | |||
} | |||
this.readOffset += bytes; | |||
return ival; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's have a consistent return result - either number
or Int64
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I wrote in the other comment, it already returns both depending on the size of the integer.
Nuclide sits at the top level, far away from |
Initially we use the timestamp that webpack provides to tell watchman to watch since this time. Afterwards we always keep the last clock value of subscription result, which is more accurate. node-int64 is a dependency now due to bug facebook/watchman#393 Fixes #11
@wez has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
* Handle changes which happen during compilation Initially we use the timestamp that webpack provides to tell watchman to watch since this time. Afterwards we always keep the last clock value of subscription result, which is more accurate. node-int64 is a dependency now due to bug facebook/watchman#393 Fixes #11 * Add test * rename test * Optimize tests, mark some lines as covered * stabilize tests * lower timeout
Thanks, would be nice if you could also make a new release. :) |
Yeah, we're just getting some things squared away; also need to release the python bindings shortly as well |
This fixes several issues with the nodejs implementation of BSER. Javascript is tricky as it treats all numbers as float, so there are some drawbacks of this implementation.
argument is out of bounds
if trying to dump32768
or2147483648
as intdump_any()
by checking if it is an integer with the polyfill outline in this MDN page.1.0
will also be detected as int because1.0 === 1
new Double()
class ornew Integer()
class, which a user would need to supply to enforce the type.readInt()
when reading an 64bit integer to check if the value can be handled by node.js. If yes it returns the primitive number otherwise returns the instance ofInt64
. This imho is nice, even if the return value in some cases could be eithernumber
orInt64
(not sure what is the actual limit)number > MAX_INT32
to be an instance of Int64. (e.g.mtime_ms
in watchman was always aInt64
and could now be anumber
)===
Fixes #392