Ember.js: [BUG 2.15] Number literals bigger than 9 digits break

Created on 5 Sep 2017  路  14Comments  路  Source: emberjs/ember.js

This is a good one.

If you pass a numeric reference to a helper/component, it works with numbers up to 9 digits.
Number with 10 or 11 digits break glimmer with TypeError: reference.value is not a function
If the number has 12 digits it doesn't throw an exception, but the value received on the other side of the reference is undefined

I believe we have an access-out-of-bounds on the glimmer VM.

Quick repo to reproduce: https://github.com/cibernox/big-num-bug

Note that this only happens with number literals {{my-helper 123123123123}} not with bound values {{my-helper num}} no matter how big the value inside num is.

Bug Has PR Has Reproduction Help Wanted

Most helpful comment

I think this is related to how the Glimmer compiler encodes primitive literals. JavaScript coerces numbers into 32-bit integers when applying bitwise operations, which we use to store type information (boolean, string, object, etc.) about the literals in the lower 3 bits.

In practice, this means that even though JavaScript allows you to represent values up to 253, our combination of a) using bitwise operators and b) utilizing the lower 3 bits means that we can't represent values greater than 229.

One way to deal with this would be to have a less-efficient primitive type (BigNum?) for >29-bit integers, although maybe it makes sense to have an external number pool just like we do with strings. Maybe @wycats or @chancancode can chime in since this is a system they came up with.

All 14 comments

I think this is related to how the Glimmer compiler encodes primitive literals. JavaScript coerces numbers into 32-bit integers when applying bitwise operations, which we use to store type information (boolean, string, object, etc.) about the literals in the lower 3 bits.

In practice, this means that even though JavaScript allows you to represent values up to 253, our combination of a) using bitwise operators and b) utilizing the lower 3 bits means that we can't represent values greater than 229.

One way to deal with this would be to have a less-efficient primitive type (BigNum?) for >29-bit integers, although maybe it makes sense to have an external number pool just like we do with strings. Maybe @wycats or @chancancode can chime in since this is a system they came up with.

FWIW, it is rather unlikely that someone will hardcode number that big in real code, such numbers will usually come from somewhere as a binding, but it's not unusual to do it on integration tests.

I made a twiddle reproduction for those interested in digging into it without cloning:

https://ember-twiddle.com/98647deda69ba3c58a58b1c6decb0add?openFiles=templates.application.hbs%2C

I think the solution to this problem is to use a constant for large numbers, just as we do for floats.

We could also use more operand slots to get a bigger number, but I'm tempted to say that the optimization at work here can be limited to relatively small (29 bit) integers.

@cibernox so you think we need to document the current upper limit for a number at 229 ? At least until there is support for a bigger integer? cc\ @locks

I'd ask first some glimmer-vm expert for an estimation of how big this change will be, and if they think it can be fixed within a few weeks, I wouldn't document it.

I think we should fix in glimmer-vm, and not have arbitrary limitations on helper arguments. However, since this is somewhat of an edge case, its likely that we won't address it in 2.15 and possibly not even in 2.16 (it depends on the amount of work to fix and backport).

We're not able to update to 2.15. Gettting TypeError: reference.value is not a function. There is a 10 digits number on our landing page. Is this really an edge case?:crying_cat_face:

screenshot

@roschaefer My temporary workaround is to put that number in the component/controller, and bind it. gets 鈧瑊{number}} every year

We have absolutely the same problem within our project integration tests.
If passed number bigger then 9 digits its just turns into undefined inside component.

{{component-to-test suggestedTargetDate=1596240000000}}

@Turbo87 @bricss @cibernox @locks @roschaefer @rwjblue @tomdale @wycats is this still an issue, perhaps we should close, what do you think?

This is still an issue: https://codesandbox.io/s/github/mike-north/octane-app-repro-cases/tree/large-number/?fontsize=14&module=%2Fapp%2Ftemplates%2Fapplication.hbs

app/templates/application.hbs
<p>
  <b>Date by number (1546416000000)</b><br>
  {{format-date 1546416000000}}
</p>
<p>
  <b>Date by string (2019-01-02T08:00:00.000Z)</b><br>
  {{format-date "2019-01-02T08:00:00.000Z"}}
</p>
app/helpers/format-date.js
import { helper } from '@ember/component/helper';

export default helper(function formatDate(params /*, hash*/) {
  return new Date(params[0]);
});

Result

Screen Shot 2019-04-24 at 11 21 57 PM

I think it would be good to fix it properly in Glimmer VM, but since it has taken so long, I would accept a Ember-side fix by AST transforming big number literals into something like (-parse-int "1546416000000"). More people should have the knowledge to write that kind of patch here on Ember side, and whenever we get to it we can fix the problem properly on glimmer side. Any takers?

Was this page helpful?
0 / 5 - 0 ratings