JDK 1.8.0_92 Индекс поведения движка Nashorn JS

Я использую движок javascript "nashorn" в java8 для оценки некоторых выражений во время выполнения. У меня есть класс утилиты для этого с методом:

    public static String evaluateJavaScriptExpression(String expression) throws ScriptException {
    if (expression == null) {
        return null;
    }
    ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
    ScriptEngine javaScriptEngine = scriptEngineManager.getEngineByName(JAVASCRIPT_ENGINE);
    return String.valueOf(javaScriptEngine.eval(expression));
}

для которого я создал несколько модульных тестов. Один из них звучит так:

    String expression = "var arr = [1, 3, 2, 5, 4]; arr.indexOf(0);";
    assertEquals("-1", ExpressionEvaluatorUtil.evaluateJavaScriptExpression(expression));

У меня все отлично работало, пока я использовал версию Java "1.8.0_91". Но кто-то, кто использовал версию java «1.8.0_92», сообщил, что тест не проходит. Я переключил свою версию на сборку 92, и она тоже не удалась. Фактический результат для него - «-1,0». Кроме того, я попробовал тот же код js в консоли Chrome, и он возвращает «-1», как в сборке 91.

Кто-нибудь знает, почему между двумя версиями jdk такая разница в результатах? Это ошибка или его специально изменили?


person dty    schedule 01.07.2016    source источник


Ответы (2)


Что ж, если вы знаете точный номер версии изменения, вы знаете, где искать: в списке исправлений 1.8u92 перечислены некоторые исправления, касающиеся Nashorn, наиболее интересным из которых является JDK-8144020:

Удалить как внутренний числовой тип

ECMA определяет double как единственный числовой тип в JavaScript. В Nashorn мы внутренне представляем числа как int, long и double. Использование long проблематично, потому что оно добавляет дополнительную точность к 53 битам, обеспечиваемым double. …

На первый взгляд, это кажется только внутренним изменением, но картина меняется, если вы понимаете, что ваш предыдущий результат связан с тем, что ранее eval возвращал Long для этого кода, отформатированного в "-1".

Теперь рассмотрим первое предложение этого отчета об ошибке: «ECMA определяет double как единственный числовой тип в JavaScript». Это приводит к выводу, что возвращение Long было не указанным типом результата, а артефактом реализации.

Таким образом, очевидно, что когда long был исключен из внутреннего использования, возможность возврата Long была исключена, и теперь механизм возвращает не Integer, а Double как побочный продукт изменения.

Это объясняет, почему существуют другие сценарии, такие как "var str = 'abcd'; str.indexOf('x');", которые по-прежнему выводят данные без дробных цифр. Последний сценарий получил оценку Integer и до сих пор оценивается. Поскольку изменение типа вывода было побочным продуктом удаления внутреннего long использования, а не преднамеренным действием по изменению всех не Double числовых результатов, места, использующие внутреннее int, не пострадали.

Сравнивая результат с движком Chrome, вы должны учитывать, что вы сравниваете только форматированный вывод, а преобразование числа в строку не является частью скрипта. Так что результат не указан. Браузер может позволить себе представить все числовые значения, соответствующие целочисленному значению, без дробных цифр.

person Holger    schedule 01.07.2016
comment
Большое спасибо за подробности. Еще два вопроса: 1) Почему var str = 'abcd'; str.indexOf('x'); оценивается как Integer, а другой код ранее оценивался как Long? 2) Учитывая, что все должно быть Double, это тоже ошибка, что str.indexOf () оценивается как Integer и будет удален таким же образом? - person dty; 01.07.2016
comment
Я не знаю, готова ли реализация массива JS обрабатывать более 2³¹ элементов или была другая причина. Я предполагаю, что когда дело доходит до String операций, где-то будет делегирование Java String.indexOf, которое вернет int. Я помню, что где-то читал, что единственный гарантированный тип возвращаемого значения для числовых результатов Нэшорна - это Number, поэтому Integer, Long или Double находятся в пределах этой спецификации. Таким образом, возврат Integer не является ошибкой, но все же может измениться в любое время. - person Holger; 04.07.2016
comment
Поэтому, если вам нужно стабильное String представление, вы должны проверить, является ли результат Number, и если да, прочитайте его doubleValue() и проверьте себя, представимо ли оно без дробной части (или всегда форматируется как double). Другими словами, нельзя полагаться на toString() реализацию возвращаемого числового типа ... - person Holger; 04.07.2016

Если мы посмотрим на java-документация для ScriptEngine .eval (строковый ввод) метод, он возвращает объект, а затем разработчик может привести объект к правильному типу в соответствии с типом возврата javascript. Итак, я думаю, что проблема в вызове метода String.valueOf (), оценка выражения возвращает число с плавающей запятой.

Я думаю, вам придется изменить свой код, чтобы сделать Integer.valueOf (javaScriptEngine.eval (expression));

Надеюсь это поможет :)

person Naresh Kumar    schedule 01.07.2016
comment
Спасибо за ваш ответ. Я думаю, вы правы, что JS возвращает число с плавающей запятой. Протестированный метод является универсальным и используется во многих местах, он не должен возвращать Integer, поэтому он всегда должен возвращать String. Я не могу понять различное поведение в сборках 91 и 92. Кроме того, у меня есть похожий тест, похожий на этот var str = 'abcd'; str.indexOf('x');, и он отлично работает, возвращенный результат действительно -1! Я нахожу это немного запутанным. - person dty; 01.07.2016