Ошибка? JsNumber toFixed возвращает разные значения в SuperDev и JS
Я использую GWT 2.8.2.
Когда я запускаю следующий код в режиме SuperDev, он регистрирует 123.456
, чего я и ожидаю.
double d = 123.456789;
JsNumber num = Js.cast(d);
console.log(num.toFixed(3));
Когда я компилирую в JavaScript и запускаю, он регистрирует 123
(т.е. он не показывает десятичные разряды).
Я попытался запустить код на Android Chrome, Windows Chrome и Windows Firefox. Все они демонстрируют одинаковое поведение.
Любая идея, почему есть разница и могу ли я что-нибудь с этим сделать?
Обновление: после еще нескольких копаний я обнаружил, что это связано с приведением целочисленного параметра.
console.log(num.toFixed(3)); // 123 (wrong)
console.log(num.toFixed(3d)); // 123.456 (correct)
Кажется, что JsNumber
класс в Elemental2 определил подпись как:
public native String toFixed(Object digits);
Я думаю, что это должно быть:
public native String toFixed(int digits);
Я до сих пор не уверен, почему он работает в режиме SuperDev, а не при компиляции.
2 ответа
Хорошо поймал! Похоже, это ошибка в конфигурации генератора jsinterop, используемой при генерации исходников Elemental2. Поскольку у JS нет способа сказать, что число является целым числом или значением с плавающей запятой, исходный материал, с которым работает jsinterop-generator, не может точно описать, каким должен быть этот аргумент.
Обычно исправление заключается в добавлении этого в integer-entity.txt ( https://github.com/google/elemental2/blob/master/java/elemental2/core/integer_entities.txt), чтобы генератор знал, что это Параметр может быть только целым числом. Однако, когда я сделал это изменение, генератор не работал на новой линии, и зарегистрировал этот факт. Оказывается, он делает это изменение только тогда, когда параметр является числом некоторого вида, которое Object
явно нет.
Правильное исправление также, вероятно, заключается в исправлении внешних параметров, которые используются для описания того, что "JsNumber.toFixed" должен принимать в качестве аргумента. В спецификации сказано, что на самом деле это может принимать не числовое значение, и после преобразования в число даже не нужно быть целым числом (см. https://www.ecma-international.org/ecma-262/5.1/ и https://www.ecma-international.org/ecma-262/5.1/).
Таким образом, вместо этого мы должны быть уверены, что передали бы любое буквальное значение, которое разработчик Java предоставляет функции, чтобы она правильно анализировалась в JS - это означает, что аргумент должен быть аннотирован с помощью @DoNotAutobox
, Или мы могли бы уточнить это, чтобы сказать, что это может быть либо Object, либо Number для аргумента, и toFixed(Object) по-прежнему будет генерироваться, но теперь будет также числовая версия.
Кроме того, вы можете обойти это, как вы сделали, или предоставив строковое значение количества цифр, которые вы хотите:
console.log(num.toFixed("3"));
Зарегистрировано как https://github.com/google/elemental2/issues/129
Проблема в том, что "Java" автоматически оборачивает int
как целое число, и GWT заканчивают тем, что переносят упакованное целое как специальный объект в JS (не число). Но если вы используете двойное число, двойное число в штучной упаковке также переносится GWT как собственное число, и проблема исчезает.
Я не совсем уверен, почему это работает в супер-devmode, но это не должно. Я думаю, что разница в том, что SDM отображает нативный toString в Java toString, и (даже более странно) нативный toFixed вызывает toString аргумента. В SDM boxed-interger#toString возвращает строковое представление числа, которое заканчивается приведением к int, но в производстве boxed-interger#toString возвращает "[объект Object]", который обрабатывается как NaN.
Есть специальная аннотация @DoNotAutobox
чтобы иметь возможность использовать примитивные целые числа в нативных API JS. Это предотвращает целочисленную автоматическую перенос, поэтому int переносится на собственный номер (пример использования в методе Js#coerceToInt). Elemental2 может добавить эту аннотацию или изменить тип на int, как вы предлагаете. Пожалуйста, создайте проблему в репозитории elemental2, чтобы исправить это ( https://github.com/google/elemental2/issues/new).